Express與Koa中介軟體機制分析(二)
在Express與Koa中介軟體機制分析(一) 中我們有提到,Express 為線型模型,而 Koa 則為洋蔥型模型,之前我們已經通過解析 connect 的原始碼對 Express 中介軟體機制進行了分析,本篇文章我們將對 Koa 的部分原始碼進行分析以幫助大家來理解其中介軟體機制。
koa1 基於的 co 庫,所以 koa1 利用 Generator 來代替回撥,而 koa2 由於 node 對 async/await 的支援,所以 koa2 利用的是 async/await
目前大家常用的基本上都是 koa2,在這裡我們先對於 Koa2 的實現進行分析,Koa1 先留個坑,之後再補充。
Koa1 中介軟體
留坑待續...
Koa2 中介軟體
Koa2 中介軟體的實現依賴於其自身的 koa-compose,下面我們看一下 compose 函式的實現。
function compose (middleware) { // 判斷引數是否合法,middleware 要求為陣列且其中每個陣列元素都為 function if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!') for (const fn of middleware) { if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!') } /** * @param {Object} context * @return {Promise} * @api public */ return function (context, next) { // last called middleware # let index = -1 // 遞迴返回一個函式 該函式返回一個 Promise 的物件 return dispatch(0) function dispatch (i) { // 當 next 方法被多次呼叫時會出現 if (i <= index) return Promise.reject(new Error('next() called multiple times')) index = i let fn = middleware[i] // 最後一箇中間件 if (i === middleware.length) fn = next if (!fn) return Promise.resolve() // Promise 封裝中介軟體 進行遞迴呼叫 try { return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); } catch (err) { return Promise.reject(err) } } } } 複製程式碼
在 Koa 中的 use 函式內,中介軟體入棧,在 compose 函式中傳入的 middleware 即為中介軟體陣列。在 compose 中對該陣列進行遞迴呼叫,返回一個 Promise 鏈。
下面通過一個例子來對中介軟體執行這一過程進行分析:
// 中介軟體 fn1 和 fn2 async function fn1 (ctx, next) { console.log('first: start') await next() console.log('first: end') } async function fn2 (ctx, next) { console.log('second: start') await next() console.log('second: end') } // 模擬中介軟體陣列 const arr = [fn1, fn2] // 執行函式,這裡返回的是一個 Promise 物件 compose(arr)() // 執行後的結果 // first: start // second: start // second: end // first: end 複製程式碼
其實,在 compose 內部遞迴執行的操作後,形成多個 Promise 層層巢狀(如下面程式碼所示),此時 next 函式其實就是下一個中介軟體,await 需要等待內部的 Promise ,所以其執行結果會呈現一個剝洋蔥的模式。
function mycompose() { return function () { const ctx = {} return Promise.resolve(fn1(ctx, () => { return Promise.resolve(fn2(ctx, () => { })) })) } } mycompose()() 複製程式碼
總結
Koa 相對於 Express 來說輕量了好多,它只是一個基礎的結構,當需要到其他功能時,再使用相應的中介軟體,上文所講到的 koa-compose 就是其中之一,其路由及其他功能又會在其他中介軟體實現,所以你可以看到,即使是 koa-compose 全部的程式碼依然很簡練。