摘要:設置了一個出事索引值調用函數,開始時候傳進去聲明函數,把傳進來的賦值給拿出第個中間件函數,賦值給判斷如果等于的長度就把賦值給如果是假的返回一個同時執行,也就是中間件,把函數傳遞到里面遞歸調用自己上面的代碼是這個部分的精華。
koa的中間件執行的流程控制,代碼的是非常精妙的。由下面的一張洋蔥模型的圖來形容,記住這張圖。
為什么是這樣子的圖,下面我們有一個例子來描述一下
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,現在只知道,調用next()函數就會把控制流程就跳到下一個中間件,知道執行所有完之后然后再逐步向上執行前一個next后面的代碼。這根跟洋蔥有很大的相像似性(如果你愿意一層一層一層的剝開我的心~~~)。
探索但是其中的原理是什么呢??下面我們一步步去探索。
首先是調用 app.use(fn) 這行代碼,這行代碼在源碼里面,刪除一些代碼判斷,是這樣子的
constructor() { super(); this.middleware = []; } use(fn) { this.middleware.push(fn); return this; }
就是把所有函數push到一個middleware的數組之中,這個use就是專門干這中勾當的。
好了知道use的作用了,執行了use之后 我們的middleware中就有很多中間件函數了,下面我們繼續看下去。
然后執行到 app.listen函數之后,代碼如下
listen(...args) { // 創建一個server const server = http.createServer(this.callback()); return server.listen(...args); }
我們看到里么有個this.callback()執行函數,然后我們跳到這個函數里面。
callback() { // 我們看這里 const fn = compose(this.middleware); const handleRequest = (req, res) => { const ctx = this.createContext(req, res); // 這個節點我們請記住下面這一行代碼 return this.handleRequest(ctx, fn); }; return handleRequest; }
這個callback函數里面,執行了compose函數,并且把middleware數組作為參數傳遞進去。
執行到了compose函數,下面我們就看看compose里面有什么。
compose函數就是一開始引用了koa-compose模塊,簡化之后發現里面的代碼如下,簡化后就簡簡單單的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) } } } }
執行這個compose返回一個函數,這也是最核心的一個函數。注意這是上面的callback調用的。得到一個fn函數
看上面的callback調用的
然后執行到this.handleRequest(ctx, fn); 這個函數吧ctx和fn(這個就是上面compose返回的函數)作為參數,傳入到this.handleRequest中。 代碼如下。
handleRequest(ctx, fnMiddleware) { return fnMiddleware(ctx).then(handleResponse).catch(onerror); }
到這里才真正的執行了compose返回的函數,把ctx傳進去。然后我們繼續看這個函數fnMiddleware(ctx),其實就是下面這樣子的。
function (context, next) { // 設置了一個出事索引值 let index = -1 // 調用dispatch函數,開始時候傳0進去 return dispatch(0) // 聲明dispatch函數, function dispatch (i) { // 把傳進來的賦值給index index = i // 拿出middleware第i個中間件函數,賦值給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, 同時執行fn, 也就是中間件,把next 函數傳遞到fn里面 return Promise.resolve(fn(context, function next () { // 遞歸調用自己 return dispatch(i + 1) })) } catch (err) { return Promise.reject(err) } } }
上面的代碼是這個部分的精華。這里詳細的說一下,首先定義了一個index和dispatch函數, 然后一開始調用dispatch(0)函數,里面把0賦值給了index,然后從middleware的數組(例子中我們有三個中間件函數)中拿到第0個中間件函數,賦值給fn,經過兩個if都不符合條件,然后執行
return Promise.resolve(fn(context, function next () { // 遞歸調用自己 return dispatch(i + 1) }))
這里的執行fn 中間件函數,并且把ctx 和 function next () { return dispatch(i + 1) }) 作為參數傳遞進去。這個時候代碼如下一幕了然
app.use(async (ctx, next) => { console.log("fn1-1") next() // 執行傳入的next console.log("fn1-2") })
執行這個函數 就會打印出fn1-1 然后就會執行next()函數,看上上一塊代碼,執行next()函數里面會調用 dispatch(i + 1) 也就是調用第fn = middleware[1] 正是第二個中間件。
看到這里大家就大概明白了。然后進入第二個中間件執行fn,打印出fn2-1,繼續執行next()函數,next函數里面繼續調用 dispatch(i + 1) ,
也就是fn = middleware[2] 第三中間件函數,打印出fn3-1,繼續執行next()函數里面會調用 dispatch(i + 1),也就是fn = middleware[3] ,
這里注意了,if (i === middleware.length) fn = next到這里會符合這個條件,然后把next 賦值給fn 這里的next就是這個fnMiddleware(ctx).then(handleResponse).catch(onerror);調用時候傳入的,然而這里并沒有傳入,所以這時候 fn 就是 undefined,然后繼續執行到if (!fn) return Promise.resolve() 返回一個空的值,這就是第三個中間件的next執行結果,
然后繼續執行下一行就打印出了fn3-2,最后向上執行到fn2-2,然后到fn1-2, 整個中間件的執行過程。很像洋蔥模型,一層層進入,然后一層層出來。
好了整個中間件執行過程就是醬紫啦~~~
最后安利一波博客: https://github.com/naihe138/n...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95475.html
摘要:閱讀好的框架的源碼有很多好處,從大神的視角去理解整個框架的設計思想。使用其實某個框架閱讀源碼的時候,首先我們要會去用這個框架,因為用了我們才知道,某個是怎么用,哪里有坑,哪里設計的精妙。 閱讀好的框架的源碼有很多好處,從大神的視角去理解整個框架的設計思想。大到架構設計,小到可取的命名風格,還有設計模式、實現某類功能使用到的數據結構和算法等等。 使用koa 其實某個框架閱讀源碼的時候,首...
摘要:閱讀好的框架的源碼有很多好處,從大神的視角去理解整個框架的設計思想。使用其實某個框架閱讀源碼的時候,首先我們要會去用這個框架,因為用了我們才知道,某個是怎么用,哪里有坑,哪里設計的精妙。 閱讀好的框架的源碼有很多好處,從大神的視角去理解整個框架的設計思想。大到架構設計,小到可取的命名風格,還有設計模式、實現某類功能使用到的數據結構和算法等等。 使用koa 其實某個框架閱讀源碼的時候,首...
摘要:最近一年零零散散看了不少開源項目的源碼多少也有點心得這里想通過這篇文章總結一下這里以為例前段時間其實看過的源碼但是發現理解的有點偏差所以重新過一遍不得不說閱讀的代碼真的收獲很大沒啥奇技淫巧代碼優雅設計極好注釋什么的就更不用說了總之還是推薦把 最近一年零零散散看了不少開源項目的源碼, 多少也有點心得, 這里想通過這篇文章總結一下, 這里以Koa為例, 前段時間其實看過Koa的源碼, 但是...
閱讀 1245·2023-04-25 18:57
閱讀 2127·2023-04-25 16:28
閱讀 3927·2021-11-24 09:39
閱讀 3631·2021-11-16 11:45
閱讀 1817·2021-10-13 09:40
閱讀 1260·2019-08-30 15:52
閱讀 1716·2019-08-30 10:57
閱讀 657·2019-08-29 16:55