欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

koa中間件核心(koa-compose)源碼解讀分析

 更新時(shí)間:2020年06月15日 09:07:53   作者:零信號(hào)  
這篇文章主要介紹了koa中間件核心(koa-compose)源碼解讀分析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

最近經(jīng)常使用koa進(jìn)行服務(wù)端開(kāi)發(fā),迷戀上了koa的洋蔥模型,覺(jué)得這玩意太好用了。而且koa是以精簡(jiǎn)為主,沒(méi)有很多集成東西,所有的東西都需按需加載,這個(gè)更是太合我胃口了哈哈哈哈。

相對(duì)與express的中間件,express的中間件使用的是串聯(lián),就像冰糖葫蘆一樣一個(gè)接著一個(gè),而koa使用的V型結(jié)構(gòu)(洋蔥模型),這將給我們的中間件提供更加靈活的處理方式。

基于對(duì)洋蔥模型的熱衷,所以對(duì)koa的洋蔥模型進(jìn)行一探究竟,不管是koa1還是koa2的中間件都是基于koa-compose進(jìn)行編寫(xiě)的,這種V型結(jié)構(gòu)的實(shí)現(xiàn)就來(lái)源于koa-compose。
附上源碼先:

function compose (middleware) {
 // 參數(shù)middleware 是一個(gè)中間件數(shù)組,存放我們用app.use()一個(gè)個(gè)串聯(lián)起來(lái)的中間件
 // 判斷中間件列表是否為數(shù)組,如果不為數(shù)組,則拋出類(lèi)型錯(cuò)誤
 if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
 // 判斷中間件是否為函數(shù),如果不為函數(shù),則拋出類(lèi)型錯(cuò)誤
 for (const fn of middleware) {
  if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
 }

 /**
 1. @param {Object} context
 2. @return {Promise}
 3. @api public
  */
 
 return function (context, next) {
  // 這里next指的是洋蔥模型的中心函數(shù)
  // context是一個(gè)配置對(duì)象,保存著一些配置,當(dāng)然也可以利用context將一些參數(shù)往下一個(gè)中間傳遞
   
  // last called middleware #
  let index = -1 // index是記錄執(zhí)行的中間件的索引
  return dispatch(0) // 執(zhí)行第一個(gè)中間件 然后通過(guò)第一個(gè)中間件遞歸調(diào)用下一個(gè)中間件
  
  function dispatch (i) {
   // 這里是保證同個(gè)中間件中一個(gè)next()不被調(diào)用多次調(diào)用 
   // 當(dāng)next()函數(shù)被調(diào)用兩次的時(shí)候,i會(huì)小于index,然后拋出錯(cuò)誤
   if (i <= index) return Promise.reject(new Error('next() called multiple times'))
   index = i
   let fn = middleware[i] // 取出要執(zhí)行的中間件
   if (i === middleware.length) fn = next // 如果i 等于 中間件的長(zhǎng)度,即到了洋蔥模型的中心(最后一個(gè)中間件)
   if (!fn) return Promise.resolve() // 如果中間件為空,即直接resolve
   try {
    // 遞歸執(zhí)行下一個(gè)中間件 (下面會(huì)重點(diǎn)分析這個(gè))
    return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
   } catch (err) {
    return Promise.reject(err)
   }
  }
 }
}

看到這里,如果下面的那些能夠理解,那么下面的可以不用看的,還是不能理解的就繼續(xù)往下看,詳細(xì)一點(diǎn)的分析。

首先,我們用app.use()添加一個(gè)中間件,在koa的源碼里app.use()這個(gè)方法就是將一個(gè)中間件push進(jìn)middleware這個(gè)中間件列表里。源碼里是這么寫(xiě)的(這個(gè)比較簡(jiǎn)單 不做分析):

 use(fn) {
  if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
  if (isGeneratorFunction(fn)) {
   deprecate('Support for generators will be removed in v3. ' +
        'See the documentation for examples of how to convert old middleware ' +
        'https://github.com/koajs/koa/blob/master/docs/migration.md');
   fn = convert(fn);
  }
  debug('use %s', fn._name || fn.name || '-');
  this.middleware.push(fn);
  return this;
 }

compose這個(gè)方法傳入一個(gè)中間件列表middleware,這個(gè)列表就是我們使用use()添加進(jìn)去的方法列表,首先會(huì)判斷列表是否為數(shù)組,中間件是否為方法,如果不是就直接拋出類(lèi)型錯(cuò)誤。

  1. compose返回的是一個(gè)函數(shù),這里使用閉包來(lái)緩存中間件列表,然后這個(gè)函數(shù)接收兩個(gè)參數(shù),第一個(gè)參數(shù)是context,是一個(gè)存放配置信息的對(duì)象。第二份參數(shù)是一個(gè)next方法,也是洋蔥模型的中心或者說(shuō)是V型模型的拐點(diǎn)。
  2. 創(chuàng)建一個(gè)index變量來(lái)保存執(zhí)行的中間件索引,然后從第一個(gè)中間件開(kāi)始開(kāi)始遞歸執(zhí)行。
let index = -1
return dispatch(0)

dispatch方法就是執(zhí)行中間件,先判斷索引,如果i小于index那么說(shuō)明在同一個(gè)中間件里執(zhí)行了兩次或兩次以上的next函數(shù),如果i>index則說(shuō)明該中間件還未執(zhí)行,那么將該中間件的所以記錄下來(lái)

if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i

取出該中間件,如果i等于中間件的長(zhǎng)圖,則說(shuō)明執(zhí)行到了洋蔥模型的中心,則最后一個(gè)中間件,如果中間件為空,那么就直接resovle掉

    let fn = middleware[i]
    if(i === middleware.length){
     fn = next
    }
    if(!fn){
      return Promise.resolve()
    }

到了最刺激的部分了,也是有點(diǎn)繞的部分了,首先為啥return的是一個(gè)Promise的對(duì)象(Promise.resolve也是一個(gè)promise對(duì)象)呢,因?yàn)槲覀僡wait next()的時(shí)候,await是等待且執(zhí)行一個(gè)async函數(shù)的完成,async會(huì)默認(rèn)返回一個(gè)promise對(duì)象,所以這里return的是一個(gè)promise對(duì)象。我們?cè)诿總€(gè)中間里面await mext() next()指的就是下一個(gè)中間件,也就是

fn(context, function next () {
      return dispatch(i + 1)
     })

所以我們上一個(gè)中的await 等待的就是dispatch(i+1)的執(zhí)行完成,dispatch返回的是Promise.resolve(fn(context, function next () { xxxx })),這樣來(lái)看雖然一開(kāi)始只執(zhí)行了dispatch(0),但是是由這個(gè)函數(shù)形成了一條執(zhí)行鏈。

以三個(gè)中間件執(zhí)行為例,dispatch(0)執(zhí)行后就形成:

Promise.resolve( // 第一個(gè)中間件
 function(context,next){ // 這里的next第二個(gè)中間件也就是dispatch(1)
   // await next上的代碼 (中間件1)
  await Promise.resolve( // 第二個(gè)中間件
   function(context,next){ // 這里的next第二個(gè)中間件也就是dispatch(2)
     // await next上的代碼 (中間件2)
    await Promise.resolve( // 第三個(gè)中間件
     function(context,next){ // 這里的next第二個(gè)中間件也就是dispatch(3)
       // await next上的代碼 (中間件3)
      await Promise.resolve()
      // await next下的代碼 (中間件3)
     }
    )
     // await next下的代碼 (中間件2)
   }
  )
   // await next下的代碼 (中間件2)
 }
) 

先執(zhí)行await上面的代碼,然后等待最后一個(gè)中間件resolve一個(gè)個(gè)往上傳遞,這就形成了一個(gè)洋蔥模型。
最后附上測(cè)試代碼:

async function test1(ctx, next) {
  console.log('中間件1上');
  await next();
  console.log('中間件1下');
 };
 
 async function test2(ctx, next) {
  console.log('中間件2上');
  await next();
  console.log('中間件2下');
 };
 
 async function test3(ctx, next) {
  console.log('中間件3上');
  await next();
  console.log('中間件3下');
 };
 let middleware = [test1, test2, test3];
 
 let cp = compose(middleware);
 
 cp('ctx', function() {
  console.log('中心');
 });

OK,到這里koa2的中間件核心(koa-compose)就解析完成了,一開(kāi)始看的時(shí)候,也被繞了好久,多看幾遍多分析一步一步捋順。koa1的中間件等過(guò)幾天有時(shí)間再補(bǔ)上吧,koa1是基于generator,源碼比起koa2相對(duì)簡(jiǎn)單。

最近在看koa2源碼,等有時(shí)間再繼續(xù)更新koa一些源碼的分析。

到此這篇關(guān)于koa中間件核心(koa-compose)源碼解讀分析的文章就介紹到這了,更多相關(guān)koa中間件核心內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Mongoose中document與object的區(qū)別示例詳解

    Mongoose中document與object的區(qū)別示例詳解

    這篇文章主要給大家介紹了關(guān)于Mongoose中document與object區(qū)別的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-09-09
  • 簡(jiǎn)單易懂的nvm和Node.js版本控制的實(shí)現(xiàn)

    簡(jiǎn)單易懂的nvm和Node.js版本控制的實(shí)現(xiàn)

    NVM是Node.js的版本管理工具,可以方便地在不同版本的Node.js之間切換,本文主要介紹了簡(jiǎn)單易懂的nvm和Node.js版本控制的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-10-10
  • nvm版本導(dǎo)致npm?install報(bào)錯(cuò)Unexpected?token?'.'的解決辦法

    nvm版本導(dǎo)致npm?install報(bào)錯(cuò)Unexpected?token?'.'的解決辦法

    最近做項(xiàng)目遇到npm install 的問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于nvm版本導(dǎo)致npm?install報(bào)錯(cuò)Unexpected?token?'.'的解決辦法,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • 基于Node.js模板引擎教程-jade速學(xué)與實(shí)戰(zhàn)1

    基于Node.js模板引擎教程-jade速學(xué)與實(shí)戰(zhàn)1

    下面小編就為大家?guī)?lái)一篇基于Node.js模板引擎教程-jade速學(xué)與實(shí)戰(zhàn)1。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • WebSocket Node構(gòu)建HTTP隧道實(shí)現(xiàn)實(shí)例

    WebSocket Node構(gòu)建HTTP隧道實(shí)現(xiàn)實(shí)例

    這篇文章主要為大家介紹了WebSocket Node構(gòu)建HTTP隧道實(shí)現(xiàn)實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • node.js部署之啟動(dòng)后臺(tái)運(yùn)行forever的方法

    node.js部署之啟動(dòng)后臺(tái)運(yùn)行forever的方法

    今天小編就為大家分享一篇node.js部署之啟動(dòng)后臺(tái)運(yùn)行forever的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • Nodejs異步流程框架async的方法

    Nodejs異步流程框架async的方法

    這篇文章主要介紹了Nodejs異步流程框架async的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-06-06
  • 基于npm?install或run時(shí)一些報(bào)錯(cuò)的解決方案

    基于npm?install或run時(shí)一些報(bào)錯(cuò)的解決方案

    這篇文章主要介紹了基于npm?install或run時(shí)一些報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Node.js學(xué)習(xí)教程之Module模塊

    Node.js學(xué)習(xí)教程之Module模塊

    這篇文章主要給大家介紹了關(guān)于Node.js學(xué)習(xí)教程之Module模塊的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Node.js具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • nodejs npm包管理的配置方法及常用命令介紹

    nodejs npm包管理的配置方法及常用命令介紹

    這篇文章主要介紹了nodejs npm包管理的配置方法及常用命令介紹,需要的朋友可以參考下
    2014-06-06

最新評(píng)論