摘要:現(xiàn)在我們從實現(xiàn)一個簡易的方法開始探索其中的機制。其中內(nèi)部的可以將上一個的返回值傳遞給外部。一言以蔽之實現(xiàn)了遞歸調(diào)用的方法。當(dāng)執(zhí)行到的中間件沒有時并且返回的為時逆序執(zhí)行。
本文發(fā)布在github.com/ssssyoki,歡迎star,issues共同交流。
Koa是基于Node.js的下一代web開發(fā)框架,相比Express更輕,源碼只有幾百行。與傳統(tǒng)的中間件不同,在Koa 1.x中采用了generator實現(xiàn)中間件,這需要開發(fā)者熟悉ES6中的generator,Promise相關(guān)知識。
在Koa官方文檔示例代碼中,采用yield next為跳轉(zhuǎn)信號,然后會逆序執(zhí)行中間件剩下的代碼邏輯。這其中的邏輯非常有趣,本文將對其進行簡要的分析。
Section A:Koa的中間件跑在co模塊下,而co可以將異步“變?yōu)椤蓖?,從而實現(xiàn)用同步的方法寫異步代碼,避免了Node.js大量的回調(diào)嵌套?,F(xiàn)在我們從實現(xiàn)一個簡易的co方法開始探索其中的機制。
function co(generator){ let g = generator(); let next = function(data){ let result = g.next(data); if(result.done){ return ; }; if(result.value instanceof Promise){ result.value.then(function(d){ next(d); },function(err){ next(err); }); }else{ next(); }; }; next(); };
首先需要了解generator相關(guān)知識,接下來我們分析這段代碼:
首先定義一個參數(shù)為generator的co函數(shù),當(dāng)傳入generator后(即app.use(function *(){...}))定義next方法實現(xiàn)對generator(可以理解為狀態(tài)機)的狀態(tài)遍歷,由于每次遍歷器指向新的yield,返回結(jié)構(gòu)如{value:"Promise","done":"true/false"}的值,當(dāng)done的值為false時遍歷狀態(tài)完畢并返回,若為true則繼續(xù)遍歷。其中內(nèi)部的g.next(data)可以將上一個yield的返回值傳遞給外部。同時,若generator中含有多個yield且遍歷未完成(即result.value是Promise對象 && result.done === false),resolve()所傳遞的數(shù)據(jù)可以在接下來then()方法中直接使用,即遞歸調(diào)用,直到result.done === true遍歷結(jié)束并退出。
這里可能存在一個疑惑,在第一次調(diào)用next()方法時data為undefined,那是否會導(dǎo)致error產(chǎn)生呢?其實V8引擎在執(zhí)行時,會自動忽略第一次調(diào)用next()時的參數(shù),所以只有從第二次使用next()方法時參數(shù)才是有效的。
一言以蔽之,co實現(xiàn)了Promise遞歸調(diào)用generator的next方法。
理解了co的運行原理后,再來理解middleware的機制就容易多了。
middleware實現(xiàn)了所謂“逆序”執(zhí)行,其實就是每次調(diào)用use()方法時,將generator存入數(shù)組(記為s)中保存。在執(zhí)行的時候先定義一個執(zhí)行索引(記為index)和跳轉(zhuǎn)標(biāo)記(記為turn,也就是yield next中的next),再定義一個保存generator函數(shù)對象的數(shù)組(記為gs)。然后獲取當(dāng)前中間件generator,接著獲取該generator的函數(shù)對象,將函數(shù)對象放在gs數(shù)組內(nèi)保存,再執(zhí)行g(shù)enerator的next()方法。
執(zhí)行開始后,根據(jù)返回的value進行不同的處理,如果是標(biāo)記turn(即執(zhí)行到了yield next),說明該跳到下一個中間件了,此時令index++,然后從數(shù)組g中獲取下一個中間件重復(fù)上一個中間件的執(zhí)行流程。
當(dāng)執(zhí)行到的中間件沒有yield時,并且返回的done為true時,逆序執(zhí)行。從此前用于保存generator函數(shù)對象的gs數(shù)組中取出上一個generator對象,然后執(zhí)行g(shù)enerator的next()方法,直到全部結(jié)束。
我們打開Koa的application.js文件:
/** * Use the given middleware "fn". * * @param {GeneratorFunction} fn * @return {Application} self * @api public */ app.use = function(fn){ if (!this.experimental) { // es7 async functions are not allowed, // so we have to make sure that "fn" is a generator function assert(fn && "GeneratorFunction" == fn.constructor.name, "app.use() requires a generator function"); } debug("use %s", fn._name || fn.name || "-"); this.middleware.push(fn); return this; };
顯而易見,app.use()方法就是將generator傳入this.middleware數(shù)組中。其他部分的邏輯源碼注釋非常清晰,不再贅述。
我們再打開Koa-compose模塊的index.js文件:
/** * Compose `middleware` returning * a fully valid middleware comprised * of all those which are passed. * * @param {Array} middleware * @return {Function} * @api public */ function compose(middleware){ return function *(next){ if (!next) next = noop(); var i = middleware.length; while (i--) { next = middleware[i].call(this, next); } return yield *next; } }
其中最關(guān)鍵的就是while語句。將之前app.use()傳入并存儲在middleware中的generator逆序取出并執(zhí)行,將每個generator執(zhí)行后的結(jié)果(即generator() === iterator)作為參數(shù)傳入下一個(按數(shù)組的順序則為前一個)generator中,在最后一個generator(數(shù)組第一個)執(zhí)行后得出的next變量(即第一個generator的iterator),執(zhí)行yield *next(即執(zhí)行第一個generator的iterator)將全部generator像鏈表般串聯(lián)起來。根據(jù)yield *的特性,yield *next將依次執(zhí)行所有套用的next(類似遞歸),從而形成所謂“正序執(zhí)行再逆序執(zhí)行”的流程。
從co到compose,代碼只有短短幾十行,但組合在一起卻非常精巧奇妙,值得細細品味。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/91368.html
摘要:代碼根據(jù)服務(wù)提供者和服務(wù)調(diào)用方法名,獲取。代碼根據(jù)服務(wù)提供者配置的最大并發(fā)度,創(chuàng)建該服務(wù)該方法對應(yīng)的信號量對象??偨Y(jié)是控制消費端對單個服務(wù)提供者單個服務(wù)允許調(diào)用的最大并發(fā)度。 本文將詳細分析< dubbo:service executes=/>與< dubbo:reference actives = />的實現(xiàn)機制,深入探...
閱讀 2014·2023-04-25 16:53
閱讀 1457·2021-10-13 09:39
閱讀 620·2021-09-08 09:35
閱讀 1653·2019-08-30 13:03
閱讀 2133·2019-08-30 11:06
閱讀 1842·2019-08-30 10:59
閱讀 3200·2019-08-29 17:00
閱讀 2301·2019-08-23 17:55