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

資訊專欄INFORMATION COLUMN

一步步去閱讀koa源碼,中間件執(zhí)行原理

WelliJhon / 1256人閱讀

摘要:設置了一個出事索引值調(diào)用函數(shù),開始時候傳進去聲明函數(shù),把傳進來的賦值給拿出第個中間件函數(shù),賦值給判斷如果等于的長度就把賦值給如果是假的返回一個同時執(zhí)行,也就是中間件,把函數(shù)傳遞到里面遞歸調(diào)用自己上面的代碼是這個部分的精華。

koa的中間件執(zhí)行的流程控制,代碼的是非常精妙的。由下面的一張洋蔥模型的圖來形容,記住這張圖。

為什么是這樣子的圖,下面我們有一個例子來描述一下

const Koa = require("koa")
const app = new Koa()
// fn1
app.use(async (ctx, next) => {
  console.log("fn1-1")
  next()
  console.log("fn1-2")
})

// fn2
app.use(async (ctx, next) => {
  console.log("fn2-1")
  next()
  console.log("fn2-2")
})

// fn3
app.use(async (ctx, next) => {
  console.log("fn3-1")
  next()
  console.log("fn3-2")
})

app.listen(4002)

// fn1-1、fn2-1、fn3-1、fn3-2、fn2-2、fn1-2

上面的這個例子,順序打印出來的是fn1-1、fn2-1、fn3-1、fn3-2、fn2-2、fn1-2,現(xiàn)在只知道,調(diào)用next()函數(shù)就會把控制流程就跳到下一個中間件,知道執(zhí)行所有完之后然后再逐步向上執(zhí)行前一個next后面的代碼。這根跟洋蔥有很大的相像似性(如果你愿意一層一層一層的剝開我的心~~~)。

探索

但是其中的原理是什么呢??下面我們一步步去探索。

首先是調(diào)用 app.use(fn) 這行代碼,這行代碼在源碼里面,刪除一些代碼判斷,是這樣子的

constructor() {
  super();
  this.middleware = [];
}

use(fn) {
  this.middleware.push(fn);
  return this;
}

就是把所有函數(shù)push到一個middleware的數(shù)組之中,這個use就是專門干這中勾當?shù)摹?/p>

好了知道use的作用了,執(zhí)行了use之后 我們的middleware中就有很多中間件函數(shù)了,下面我們繼續(xù)看下去。

然后執(zhí)行到 app.listen函數(shù)之后,代碼如下

listen(...args) {
  // 創(chuàng)建一個server
  const server = http.createServer(this.callback());
  return server.listen(...args);
}

我們看到里么有個this.callback()執(zhí)行函數(shù),然后我們跳到這個函數(shù)里面。

callback() {
  // 我們看這里
  const fn = compose(this.middleware);

  const handleRequest = (req, res) => {
    const ctx = this.createContext(req, res);
    // 這個節(jié)點我們請記住下面這一行代碼
    return this.handleRequest(ctx, fn);
  };

  return handleRequest;
}

這個callback函數(shù)里面,執(zhí)行了compose函數(shù),并且把middleware數(shù)組作為參數(shù)傳遞進去。

執(zhí)行到了compose函數(shù),下面我們就看看compose里面有什么。

compose函數(shù)就是一開始引用了koa-compose模塊,簡化之后發(fā)現(xiàn)里面的代碼如下,簡化后就簡簡單單的20幾行代碼,后面會詳細解釋下面的代碼。

function compose (middleware) {
  return function (context, next) {
    let index = -1
    return dispatch(0)
    function dispatch (i) {
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      if (!fn) return Promise.resolve()
      try {
        return Promise.resolve(fn(context, function next () {
          return dispatch(i + 1)
        }))
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

執(zhí)行這個compose返回一個函數(shù),這也是最核心的一個函數(shù)。注意這是上面的callback調(diào)用的。得到一個fn函數(shù)

看上面的callback調(diào)用的

然后執(zhí)行到this.handleRequest(ctx, fn); 這個函數(shù)吧ctxfn(這個就是上面compose返回的函數(shù))作為參數(shù),傳入到this.handleRequest中。 代碼如下。

handleRequest(ctx, fnMiddleware) {
  return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}

到這里才真正的執(zhí)行了compose返回的函數(shù),把ctx傳進去。然后我們繼續(xù)看這個函數(shù)fnMiddleware(ctx),其實就是下面這樣子的。

function (context, next) {
  // 設置了一個出事索引值
  let index = -1
  // 調(diào)用dispatch函數(shù),開始時候傳0進去
  return dispatch(0) 

  // 聲明dispatch函數(shù),
  function dispatch (i) {
    // 把傳進來的賦值給index
    index = i
    // 拿出middleware第i個中間件函數(shù),賦值給fn
    let fn = middleware[i] 
    // 判斷如果i 等于middleware的長度 就把next 賦值給 fn
    if (i === middleware.length) fn = next
    // 如果fn是假的 return return Promise.resolve()
    if (!fn) return Promise.resolve()
    try {
      // 返回一個Promise.resolve, 同時執(zhí)行fn, 也就是中間件,把next 函數(shù)傳遞到fn里面 
      return Promise.resolve(fn(context, function next () {
        // 遞歸調(diào)用自己
        return dispatch(i + 1)
      }))
    } catch (err) {
      return Promise.reject(err)
    }
  }
}

上面的代碼是這個部分的精華。這里詳細的說一下,首先定義了一個indexdispatch函數(shù), 然后一開始調(diào)用dispatch(0)函數(shù),里面把0賦值給了index,然后從middleware的數(shù)組(例子中我們有三個中間件函數(shù))中拿到第0個中間件函數(shù),賦值給fn,經(jīng)過兩個if都不符合條件,然后執(zhí)行

return Promise.resolve(fn(context, function next () {
    // 遞歸調(diào)用自己
    return dispatch(i + 1)
  }))

這里的執(zhí)行fn 中間件函數(shù),并且把ctx function next () { return dispatch(i + 1) }) 作為參數(shù)傳遞進去。這個時候代碼如下一幕了然

app.use(async (ctx, next) => {
  console.log("fn1-1")
  next() // 執(zhí)行傳入的next
  console.log("fn1-2")
})

執(zhí)行這個函數(shù) 就會打印出fn1-1 然后就會執(zhí)行next()函數(shù),看上上一塊代碼,執(zhí)行next()函數(shù)里面會調(diào)用 dispatch(i + 1) 也就是調(diào)用第fn = middleware[1] 正是第二個中間件。

看到這里大家就大概明白了。然后進入第二個中間件執(zhí)行fn,打印出fn2-1,繼續(xù)執(zhí)行next()函數(shù),next函數(shù)里面繼續(xù)調(diào)用 dispatch(i + 1) ,

也就是fn = middleware[2] 第三中間件函數(shù),打印出fn3-1,繼續(xù)執(zhí)行next()函數(shù)里面會調(diào)用 dispatch(i + 1),也就是fn = middleware[3]

這里注意了,if (i === middleware.length) fn = next到這里會符合這個條件,然后把next 賦值給fn 這里的next就是這個fnMiddleware(ctx).then(handleResponse).catch(onerror);調(diào)用時候傳入的,然而這里并沒有傳入,所以這時候 fn 就是 undefined,然后繼續(xù)執(zhí)行到if (!fn) return Promise.resolve() 返回一個空的值,這就是第三個中間件的next執(zhí)行結果,

然后繼續(xù)執(zhí)行下一行就打印出了fn3-2,最后向上執(zhí)行到fn2-2,然后到fn1-2, 整個中間件的執(zhí)行過程。很像洋蔥模型,一層層進入,然后一層層出來。

好了整個中間件執(zhí)行過程就是醬紫啦~~~

最后安利一波博客: https://github.com/naihe138/n...

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

轉載請注明本文地址:http://systransis.cn/yun/95475.html

相關文章

  • 步步閱讀koa源碼,整體架構分析

    摘要:閱讀好的框架的源碼有很多好處,從大神的視角去理解整個框架的設計思想。使用其實某個框架閱讀源碼的時候,首先我們要會去用這個框架,因為用了我們才知道,某個是怎么用,哪里有坑,哪里設計的精妙。 閱讀好的框架的源碼有很多好處,從大神的視角去理解整個框架的設計思想。大到架構設計,小到可取的命名風格,還有設計模式、實現(xiàn)某類功能使用到的數(shù)據(jù)結構和算法等等。 使用koa 其實某個框架閱讀源碼的時候,首...

    haoguo 評論0 收藏0
  • 步步閱讀koa源碼,整體架構分析

    摘要:閱讀好的框架的源碼有很多好處,從大神的視角去理解整個框架的設計思想。使用其實某個框架閱讀源碼的時候,首先我們要會去用這個框架,因為用了我們才知道,某個是怎么用,哪里有坑,哪里設計的精妙。 閱讀好的框架的源碼有很多好處,從大神的視角去理解整個框架的設計思想。大到架構設計,小到可取的命名風格,還有設計模式、實現(xiàn)某類功能使用到的數(shù)據(jù)結構和算法等等。 使用koa 其實某個框架閱讀源碼的時候,首...

    chaos_G 評論0 收藏0
  • 如何閱讀源碼--Koa為例

    摘要:最近一年零零散散看了不少開源項目的源碼多少也有點心得這里想通過這篇文章總結一下這里以為例前段時間其實看過的源碼但是發(fā)現(xiàn)理解的有點偏差所以重新過一遍不得不說閱讀的代碼真的收獲很大沒啥奇技淫巧代碼優(yōu)雅設計極好注釋什么的就更不用說了總之還是推薦把 最近一年零零散散看了不少開源項目的源碼, 多少也有點心得, 這里想通過這篇文章總結一下, 這里以Koa為例, 前段時間其實看過Koa的源碼, 但是...

    zhoutk 評論0 收藏0

發(fā)表評論

0條評論

WelliJhon

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<