摘要:到此為止,我們就基本講清楚了中的中間件洋蔥模型是如何自動執行的。
koa被認為是第二代web后端開發框架,相比于前代express而言,其最大的特色無疑就是解決了回調金字塔的問題,讓異步的寫法更加的簡潔。在使用koa的過程中,其實一直比較好奇koa內部的實現機理。最近終于有空,比較深入的研究了一下koa一些原理,在這里會寫一系列文章來記錄一下我的學習心得和理解。
在我看來,koa最核心的函數是大名鼎鼎的co,koa正是基于這個函數實現了異步回調同步化,以及中間件流程控制。當然在這篇文章中我并不會去分析co源碼,我打算在整個系列文章中,一步一步講解如何實現koa中間件的流程控制原理,koa的異步回調同步寫法實現原理,最后在理解這些的基礎上,實現一個簡單的類似co的函數。
本篇首先只談一談koa的中間件流程控制原理。
1. koa中間件執行流程關于koa中間件如何執行,官網上有一個非常經典的例子,有興趣的可以去看看,不過這里,我想把它修改的更簡單一點:
var koa = require("koa"); var app = koa(); app.use(function*(next) { console.log("begin middleware 1"); yield next; console.log("end middleware 1"); }); app.use(function*(next) { console.log("begin middleware 2"); yield next; console.log("end middleware 2"); }); app.use(function*() { console.log("middleware 3"); }); app.listen(3000);
運行這個例子,然后使用curl工具,運行:
curl http://localhost:3000
可以看到,運行之后,會輸出:
begin middleware 1 begin middleware 2 middleware 3 end middleware 2 end middleware 1
這個例子非常形象的代表了koa的中間件執行機制,可以用下圖的洋蔥模型來形容:
通過這種執行流程,開發者可以非常方便的開發一些中間件,并且非常容易的整合到實際業務流程中。那么,這樣的流程又是如何實現和控制的呢?
2. koa中的generator和compose簡單來說,洋蔥模型的執行流程是通過es6中的generator來實現的。不熟悉generator的同學可以去看看其特性,其中一個就是generator函數可以像打斷點一樣從函數某個地方跳出,之后還可以再回來繼續執行。下面一個例子可以說明這種特性:
var gen=function*(){ console.log("begin!"); //yield語句,在這里跳出,將控制權交給anotherfunc函數。 yield anotherfunc; //下次回來時候從這里開始執行 console.log("end!"); } var anotherfunc(){ console.log("this is another function!"); } var g=gen(); var another=g.next(); //"begin!" //another是一個對象,其中value成員就是返回的anotherfunc函數 another.value(); //"this is another function!" g.next(); //"end!";
從這個簡單例子中,可以看出洋蔥模型最基本的一個雛形,即yield前后的語句最先和最后執行,yield中間的代碼在中心執行。
現在設想一下,如果yield后面跟的函數本身就又是一個generator,會怎么樣呢?其實就是從上面例子里面做一個引申:
var gen1=function*(){ console.log("begin!"); yield g2; console.log("end!"); } var gen2=function*(){ console.log("begin 2"); yield anotherfunc; console.log("end 2"); } var anotherfunc(){ console.log("this is another function!"); } var g=gen(); var g2=gen2(); var another1=g.next(); //"begin!"; var another2=another1.value.next(); //"begin 2"; another2.value(); //"this is another function!"; another1.value.next(); //"end 2"; g.next(); //"end!";
可以看出,基本上是用上面的例子,再加一個嵌套而已,原理是一樣的。
而在koa中,每個中間件generator都有一個next參數。在我們這個例子中,g2就可以看成是g函數的next參數。事實上,koa也確實是這樣做的,當使用app.use()掛載了所有中間件之后,koa有一個koa-compose模塊,用于將所有generator中間件串聯起來,基本上就是將后一個generator賦給前一個generator的next參數。koa-compose的源碼非常簡單短小,下面是我自己實現的一個:
function compose(middlewares) { return function(next) { var i = middlewares.length; var next = function*() {}(); while (i--) { next = middlewares[i].call(this, next); } return next; } }
使用我們自己寫的compose對上面一個例子改造,是的其更接近koa的形式:
function compose(middlewares) { return function(next) { var i = middlewares.length; var next = function*() {}(); while (i--) { next = middlewares[i].call(this, next); } return next; } } var gen1=function*(next){ console.log("begin!"); yield next; console.log("end!"); } var gen2=function*(next){ console.log("begin 2"); yield next; console.log("end 2"); } var gen3=function*(next){ console.log("this is another function!"); } var bundle=compose([gen1,gen2,gen3]); var g=bundle(); var another1=g.next(); //"begin!"; var another2=another1.value.next(); //"begin 2"; another2.value.next(); //"this is another function!"; another1.value.next(); //"end 2"; g.next(); //"end!";
怎么樣?是不是有一點koa中間件寫法的感覺了呢?但是目前,我們還是一步一步手動的在執行我們這個洋蔥模型,能否寫一個函數,自動的來執行我們這個模型呢?
3. 讓洋蔥模型自動跑起來:一個run函數的編寫上面例子中,最后的代碼我們可以看出一個規律,基本就是外層的generator調用next方法把控制權交給內層,內層再繼續調用next把方法交給更里面的一層。整個流程可以用一個函數嵌套的寫法寫出來。話不多說,直接上代碼:
function run(gen) { var g; if (typeof gen.next === "function") { g = gen; } else { g = gen(); } function next() { var tmp = g.next(); //如果tmp.done為true,那么證明generator執行結束,返回。 if (tmp.done) { return; } else if (typeof g.next === "function") { run(tmp.value); next(); } } next(); } function compose(middlewares) { return function(next) { var i = middlewares.length; var next = function*() {}(); while (i--) { next = middlewares[i].call(this, next); } return next; } } var gen1 = function*(next) { console.log("begin!"); yield next; console.log("end!"); } var gen2 = function*(next) { console.log("begin 2"); yield next; console.log("end 2"); } var gen3 = function*(next) { console.log("this is another function!"); } var bundle = compose([gen1, gen2, gen3]); run(bundle);
run函數接受一個generator,其內部執行其實就是我們上一個例子的精簡,使用遞歸的方法執行。運行這個例子,可以看到結果和我們上一個例子相同。
到此為止,我們就基本講清楚了koa中的中間件洋蔥模型是如何自動執行的。事實上,koa中使用的co函數,一部分功能就是實現我們這里編寫的run函數的功能。
值得注意的是,這篇文章只注重分析中間件執行流程的實現,暫時并沒有考慮異步回調同步化原理。下一篇文章中,我將帶大家繼續探析koa中異步回調同步化寫法的機理。
這篇文章的代碼可以在github上面找到:https://github.com/mly-zju/async-js-demo,其中process_control.js文件就是本篇的事例源碼。
另外歡迎多多關注我的個人博客哦^_^ 會不定期更新我的技術文章~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/80864.html
摘要:而之后,我們得到的是一個是一個對象,我們可以使用語句定義回調函數,函數的內容呢,則是將讀取到的返回給并繼續讓從斷點處執行。 在上一篇中我們梳理了koa當中中間件的洋蔥模型執行原理,并實現了一個可以讓洋蔥模型自動跑起來的流程管理函數。這一篇,我們再來研究一下koa當中異步回調同步化寫法的原理,同樣的,我們也會實現一個管理函數,是的我們能夠通過同步化的寫法來寫異步回調函數。 1. 回調金字...
摘要:本文來自心譚博客深入源碼架構設計前端面試設計模式手冊教程實戰等更多專題,請來導航頁領取食用所有系列文章都放在了。歡迎交流和最近讀了的源碼,理清楚了架構設計與用到的第三方庫。 本文來自《心譚博客·深入koa源碼:架構設計》前端面試、設計模式手冊、Webpack4教程、NodeJs實戰等更多專題,請來導航頁領取食用所有系列文章都放在了Github。歡迎交流和Star ?? ヽ(°▽°)ノ ...
摘要:需要說明的是,每次執行完函數之后,都會返回一個對象這個返回值有兩個屬性和,對象通過這個返回值來告訴外界函數的執行情況。函數的返回值變成這樣可以發現的值變為了,因為函數已經執行完了。在規范中,新增了兩個協議可迭代協議和迭代器協議。 Koa是最近比較火的一款基于Node的web開發框架。說他是一個框架,其實他更像是一個函數庫,通過某種思想(或者說某種約定),將眾多的中間件聯系在一起,從而提...
摘要:許多公司都投資于或之類的靜態分析安全測試,解決方案。用靜態分析方法確保編程安全一書詳細描述了靜態分析技術的基本原理。博士將靜態分析無法找出的諸多安全問題歸為瑕疵,而非程序錯誤。 靜態分析安全測試(SAST)是指不運行被測程序本身,僅通過分析或者檢查源程序的語法、結構、過程、接口等來檢查程序的正確性,那么采用靜分析安全測試的方法有什么優缺點呢,且讓小編給你說道說道。 許多公司都投資于 H...
摘要:感謝大神的免費的計算機編程類中文書籍收錄并推薦地址,以后在倉庫里更新地址,聲音版全文狼叔如何正確的學習簡介現在,越來越多的科技公司和開發者開始使用開發各種應用。 說明 2017-12-14 我發了一篇文章《沒用過Node.js,就別瞎逼逼》是因為有人在知乎上黑Node.js。那篇文章的反響還是相當不錯的,甚至連著名的hax賀老都很認同,下班時讀那篇文章,竟然坐車的還坐過站了。大家可以很...
閱讀 1599·2021-11-02 14:48
閱讀 3651·2019-08-30 15:56
閱讀 2767·2019-08-30 15:53
閱讀 3208·2019-08-30 14:09
閱讀 3094·2019-08-30 12:59
閱讀 2852·2019-08-29 18:38
閱讀 2692·2019-08-26 11:41
閱讀 2209·2019-08-23 16:45