koa-convert最主要的做用是:將koa1包中使用的Generator函數轉換成Koa2中的async函數。更準確的說是將Generator函數轉換成使用co包裝成的Promise對象。而後執行對應的代碼。固然該包中也提供了back方法,也能夠把koa2中的async函數轉換成koa1包中的Generator函數。html
首先咱們來看下使用Koa1中使用Generator函數和Koa2中使用的async函數的demo代碼以下:node
const Koa = require('koa'); const app = new Koa(); // koa1 使用generator函數的寫法 app.use(function *(next) { console.log(1111); // 1. 第一步先打印 1111 yield next; console.log(222222); // 4. 第四步打印 222222 }); // koa2的寫法 app.use(async (ctx, next) => { console.log(3333); // 2. 第二步再打印 3333 await next(); console.log(44444); // 3. 第三部打印44444 }); app.listen(3001); console.log('app started at port 3001...');
當咱們在node命令行中使用 node app.js 命令時,而後瀏覽器中 輸入地址:http://localhost:3001/ 訪問的時候,咱們能夠看到命令中會分別打印 1111 3333 444444 222222.git
咱們再來看下,Koa源碼中的application.js 代碼以下:在use方法內部,代碼以下:github
const convert = require('koa-convert'); 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; }
如上koa2源碼中的use函數,該函數有一個fn參數,首先判斷該fn是否是一個函數,若是不是一個函數的話,直接拋出一個錯誤,提示,中間件必須爲一個函數。第二步繼續判斷該fn函數是否是一個Generator函數,若是它是generator函數的話,就把該函數使用 koa-convert包轉換成async函數。而後把對應的async函數放進 this.middleware數組中。最後返回該對象this。數組
這上面是koa2中的基本源碼,下面咱們來看看 koa-convert中的源碼是如何作的呢?promise
const co = require('co') const compose = require('koa-compose') module.exports = convert function convert (mw) { if (typeof mw !== 'function') { throw new TypeError('middleware must be a function') } if (mw.constructor.name !== 'GeneratorFunction') { // assume it's Promise-based middleware return mw } const converted = function (ctx, next) { return co.call(ctx, mw.call(ctx, createGenerator(next))) } converted._name = mw._name || mw.name return converted } function * createGenerator (next) { return yield next() } // convert.compose(mw, mw, mw) // convert.compose([mw, mw, mw]) convert.compose = function (arr) { if (!Array.isArray(arr)) { arr = Array.from(arguments) } return compose(arr.map(convert)) }
1. 部分源碼如上,首先引入co包中的代碼,要深刻了解co包中源碼請看這篇文章, 該co的做用是:將Generator函數轉換成promise對象,而後會自動執行該函數的代碼。瀏覽器
2. 引入 koa-compose包,要深刻了解 koa-compose包,請看這篇文章. 該包的做用是:將koa包中的中間件合併,而後依次執行各個中間件。app
3. convert 函數
1. 該函數有一個參數mw,首先判斷該參數mw是否是一個函數,若是該mw不是一個函數的話,就直接拋出一個異常提示,該中間件必須是一個函數。
2. 判斷該 mw.constructor.name !== 'GeneratorFunction' 是否是一個Generator函數,若是不是Generator函數的話,就直接返回該mw。
3. 若是它是Generator函數的話,就會執行 converted 函數,該函數有2個參數,第一個參數ctx是運行Generator的上下文,第二個參數是傳遞給Generator函數的next參數。
4. 最後返回 return co.call(ctx, mw.call(ctx, createGenerator(next))); co的做用是介紹一個Generator函數,而後會返回一個Promise對象,而後該Generator函數會自動執行。createGenerator函數代碼以下:koa
function * createGenerator (next) { return yield next() }
所以 mw.call(ctx, createGenerator(next)),若是mw是一個Generator函數的話,就直接調用該Generator函數,返回return yield next(); 返回下一個中間件,而後使用調用co包,使返回一個Promise對象。該自己對象的代碼會自動執行完。async
5. 在convert函數代碼中,有一句代碼 mw.constructor.name !== 'GeneratorFunction' 是否是一個Generator函數。
能夠如上面進行判斷,好比以下代碼演示是不是Generator函數仍是AsyncFunction函數了,以下代碼:
function* test () {}; console.log(test.constructor.name); // 打印 GeneratorFunction async function test2() {}; console.log(test2.constructor.name); // 打印 AsyncFunction
如上全部的分析是 convert 函數的代碼了,該代碼一個最主要的做用,判斷傳遞進來的mw參數是否是Generator函數,若是是Generator函數的話,就把該Generator函數轉化成使用co包裝成Promise對象了。
4. back函數代碼以下:
convert.back = function (mw) { if (typeof mw !== 'function') { throw new TypeError('middleware must be a function') } if (mw.constructor.name === 'GeneratorFunction') { // assume it's generator middleware return mw } const converted = function * (next) { let ctx = this let called = false // no need try...catch here, it's ok even `mw()` throw exception yield Promise.resolve(mw(ctx, function () { if (called) { // guard against multiple next() calls // https://github.com/koajs/compose/blob/4e3e96baf58b817d71bd44a8c0d78bb42623aa95/index.js#L36 return Promise.reject(new Error('next() called multiple times')) } called = true return co.call(ctx, next) })) } converted._name = mw._name || mw.name return converted }
代碼也是同樣判斷:
1. 判斷mw是不是一個函數,若是不是一個函數,則拋出異常。
2. 判斷mw.constructor.name === 'GeneratorFunction'; 若是是Generator函數的話,就直接返回該Generator函數。
若是不是Generaror函數的話,就執行 converted 方法,轉換成Generator函數。一樣的道理調用co模塊返回一個Promise對象。
5. convert.compose函數代碼以下:
convert.compose = function (arr) { if (!Array.isArray(arr)) { arr = Array.from(arguments) } return compose(arr.map(convert)) }
該函數的做用是:就是將一系列Generator函數組成的數組,直接轉成Koa2中可執行的middleware形式。調用 koa-compose 包轉換成中間件形式。