成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

對(duì)Koa-middleware實(shí)現(xiàn)機(jī)制的分析

MageekChiu / 3045人閱讀

摘要:現(xiàn)在我們從實(shí)現(xiàn)一個(gè)簡(jiǎn)易的方法開始探索其中的機(jī)制。其中內(nèi)部的可以將上一個(gè)的返回值傳遞給外部。一言以蔽之實(shí)現(xiàn)了遞歸調(diào)用的方法。當(dāng)執(zhí)行到的中間件沒(méi)有時(shí)并且返回的為時(shí)逆序執(zhí)行。

本文發(fā)布在github.com/ssssyoki,歡迎star,issues共同交流。

Koa是基于Node.js的下一代web開發(fā)框架,相比Express更輕,源碼只有幾百行。與傳統(tǒng)的中間件不同,在Koa 1.x中采用了generator實(shí)現(xiàn)中間件,這需要開發(fā)者熟悉ES6中的generator,Promise相關(guān)知識(shí)。

在Koa官方文檔示例代碼中,采用yield next為跳轉(zhuǎn)信號(hào),然后會(huì)逆序執(zhí)行中間件剩下的代碼邏輯。這其中的邏輯非常有趣,本文將對(duì)其進(jìn)行簡(jiǎn)要的分析。

Section A:

Koa的中間件跑在co模塊下,而co可以將異步“變?yōu)椤蓖?,從而?shí)現(xiàn)用同步的方法寫異步代碼,避免了Node.js大量的回調(diào)嵌套?,F(xiàn)在我們從實(shí)現(xiàn)一個(gè)簡(jiǎn)易的co方法開始探索其中的機(jī)制。

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í),接下來(lái)我們分析這段代碼:

首先定義一個(gè)參數(shù)為generator的co函數(shù),當(dāng)傳入generator后(即app.use(function *(){...}))定義next方法實(shí)現(xiàn)對(duì)generator(可以理解為狀態(tài)機(jī))的狀態(tài)遍歷,由于每次遍歷器指向新的yield,返回結(jié)構(gòu)如{value:"Promise","done":"true/false"}的值,當(dāng)done的值為false時(shí)遍歷狀態(tài)完畢并返回,若為true則繼續(xù)遍歷。其中內(nèi)部的g.next(data)可以將上一個(gè)yield的返回值傳遞給外部。同時(shí),若generator中含有多個(gè)yield且遍歷未完成(即result.valuePromise對(duì)象 && result.done === false),resolve()所傳遞的數(shù)據(jù)可以在接下來(lái)then()方法中直接使用,即遞歸調(diào)用,直到result.done === true遍歷結(jié)束并退出。

這里可能存在一個(gè)疑惑,在第一次調(diào)用next()方法時(shí)data為undefined,那是否會(huì)導(dǎo)致error產(chǎn)生呢?其實(shí)V8引擎在執(zhí)行時(shí),會(huì)自動(dòng)忽略第一次調(diào)用next()時(shí)的參數(shù),所以只有從第二次使用next()方法時(shí)參數(shù)才是有效的。

一言以蔽之,co實(shí)現(xiàn)了Promise遞歸調(diào)用generator的next方法。

*倉(cāng)庫(kù)內(nèi)co-example.js添加了測(cè)試代碼,上述實(shí)例測(cè)試可以運(yùn)行成功。
Section B:

理解了co的運(yùn)行原理后,再來(lái)理解middleware的機(jī)制就容易多了。

middleware實(shí)現(xiàn)了所謂“逆序”執(zhí)行,其實(shí)就是每次調(diào)用use()方法時(shí),將generator存入數(shù)組(記為s)中保存。在執(zhí)行的時(shí)候先定義一個(gè)執(zhí)行索引(記為index)和跳轉(zhuǎn)標(biāo)記(記為turn,也就是yield next中的next),再定義一個(gè)保存generator函數(shù)對(duì)象的數(shù)組(記為gs)。然后獲取當(dāng)前中間件generator,接著獲取該generator的函數(shù)對(duì)象,將函數(shù)對(duì)象放在gs數(shù)組內(nèi)保存,再執(zhí)行g(shù)enerator的next()方法。
執(zhí)行開始后,根據(jù)返回的value進(jìn)行不同的處理,如果是標(biāo)記turn(即執(zhí)行到了yield next),說(shuō)明該跳到下一個(gè)中間件了,此時(shí)令index++,然后從數(shù)組g中獲取下一個(gè)中間件重復(fù)上一個(gè)中間件的執(zhí)行流程。

當(dāng)執(zhí)行到的中間件沒(méi)有yield時(shí),并且返回的donetrue時(shí),逆序執(zhí)行。從此前用于保存generator函數(shù)對(duì)象的gs數(shù)組中取出上一個(gè)generator對(duì)象,然后執(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ù)組中。其他部分的邏輯源碼注釋非常清晰,不再贅述。

我們?cè)俅蜷_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語(yǔ)句。將之前app.use()傳入并存儲(chǔ)在middleware中的generator逆序取出并執(zhí)行,將每個(gè)generator執(zhí)行后的結(jié)果(即generator() === iterator)作為參數(shù)傳入下一個(gè)(按數(shù)組的順序則為前一個(gè))generator中,在最后一個(gè)generator(數(shù)組第一個(gè))執(zhí)行后得出的next變量(即第一個(gè)generator的iterator),執(zhí)行yield *next(即執(zhí)行第一個(gè)generator的iterator)將全部generator像鏈表般串聯(lián)起來(lái)。根據(jù)yield *的特性,yield *next將依次執(zhí)行所有套用的next(類似遞歸),從而形成所謂“正序執(zhí)行再逆序執(zhí)行”的流程。

從co到compose,代碼只有短短幾十行,但組合在一起卻非常精巧奇妙,值得細(xì)細(xì)品味。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/91368.html

相關(guān)文章

  • Dubbo 源碼分析20 Dubbo服務(wù)提供者、服務(wù)消費(fèi)者并發(fā)度控制機(jī)制

    摘要:代碼根據(jù)服務(wù)提供者和服務(wù)調(diào)用方法名,獲取。代碼根據(jù)服務(wù)提供者配置的最大并發(fā)度,創(chuàng)建該服務(wù)該方法對(duì)應(yīng)的信號(hào)量對(duì)象??偨Y(jié)是控制消費(fèi)端對(duì)單個(gè)服務(wù)提供者單個(gè)服務(wù)允許調(diào)用的最大并發(fā)度。 本文將詳細(xì)分析< dubbo:service executes=/>與< dubbo:reference actives = />的實(shí)現(xiàn)機(jī)制,深入探...

    不知名網(wǎng)友 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<