| | | middleware 1 | | | | +-----------------------------------------------------------+ | | | | | | | middleware 2 | | | | | | | | +---------------------------------+ | | | | | | | | | action | action | middleware 3 | action | action | | 001 | 002 | | 005 | 006 | | | | action action | | | | | | 003 004 | | | | | | | | |
+---------------------------------------------------------------------------------------------------->html
| | | | | | | | | | | | | | +---------------------------------+ | | | +-----------------------------------------------------------+ | +----------------------------------------------------------------------------------+
先写一段贯穿全文的koa的代码git
const Koa = require('koa'); let app = new Koa(); const middleware1 = async (ctx, next) => { console.log(1); await next(); console.log(6); } const middleware2 = async (ctx, next) => { console.log(2); await next(); console.log(5); } const middleware3 = async (ctx, next) => { console.log(3); await next(); console.log(4); } app.use(middleware1); app.use(middleware2); app.use(middleware3); app.use(async(ctx, next) => { ctx.body = 'hello world' }) app.listen(3001) // 输出1,2,3,4,5,6
await next()
使每一个middleware分红,前置操做,等待其余中间件操做能够观察到中间件的特性有:github
Promise.resolve(middleware1(context, async() => { return Promise.resolve(middleware2(context, async() => { return Promise.resolve(middleware3(context, async() => { return Promise.resolve(); })); })); })) .then(() => { console.log('end'); });
从这段模拟代码咱们能够知道next()返回的是promise,须要使用await去等待promise的resolve值。promise的嵌套就像是洋葱模型的形状就是一层包裹着一层,直到await到最里面一层的promise的resolve值返回。segmentfault
思考:数组
next()
执行顺序跟await next()
是同样的,由于next的前置操做是同步的若是前置操做是异步的操做呢?promise
const p = function(args) { return new Promise(resolve => { setTimeout(() => { console.log(args); resolve(); }, 100); }); }; const middleware1 = async (ctx, next) => { await p(1); // await next(); next(); console.log(6); }; const middleware2 = async (ctx, next) => { await p(2); // await next(); next(); console.log(5); }; const middleware3 = async (ctx, next) => { await p(3); // await next(); next(); console.log(4); }; // 输出结果:1,6,2,5,3,4
当程序执行到middleware1,执行到await p(1)
等待promise值返回跳出而后到下一个事件循环时,执行next()也就是执行到middleware2,再执行到await p(2)
等待promise值返回跳出middleware2,回到middleware1继续执行console.log(6),以此类推输出顺序为1.6.2.5.3.4app
Promise的嵌套虽然能够实现中间件流程,可是嵌套的代码会产生可维护性和可读性的问题,也带来中间件扩展的问题。koa
Koa.js中间件引擎是有koa-compose模块来实现的,也就是Koa.js实现洋葱模型的核心引擎。异步
this.middleware = []; use(fn) { this.middleware.push(fn); …… } callback() { const fn = compose(this.middleware); …… } function compose (middleware) { return function (context, next) { let index = -1 return dispatch(0) function dispatch (i) { 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() try { return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); } catch (err) { return Promise.reject(err) } } } }
Koa实现的代码很是简洁,咱们在使用use的时候将middleware存在一个数组里面,当拦截到请求时执行callback方法,callback中调用了compose,compose方法使用递归执行中间件,遍历完成返回promise.resolve()
,实际最后执行的代码也是上面所讲的promise嵌套的形式。async
一般咱们的都会说await阻塞后面的操做等待promise的resolve返回值或者其余值,若是没有await这个语法糖,要怎么去实现呢?这个等待的过程是怎么控制的呢?
Generator其实是一个特殊的迭代器
let gen = null; function* genDemo(){ console.log(1) yield setTimeout(()=>{ console.log(3); gen.next();// c },100) console.log(4) } gen = genDemo();// a gen.next(); // b
a. 调用generator,该函数不执行,也就是尚未输出1,返回的是指向内部状态的遍历对象。
b. generator函数开始执行,输出1,遇到第一个yeild表达式停下来,调用gen.next()返回一个对象{value: 10, done:false},这里的value表示setTimeout的一个标识值,也就是调用clearTimeout的参数,是一个数字。done表示遍历尚未结束。100毫秒后输出3;
c. Generator函数从上次在yeild中止的地方一直执行到函数结束(没有其余的yeild),输出4,返回{value: undefined,done:true},表示遍历结束。
能够看到yeild有控制代码进度的做用,是否是跟await有殊途同归之妙
来看下await编译成generator形式的代码,虽然多了一些代码,可是咱们能够把_asyncToGenerator(function*() {……}
调用generator,把asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
当作是gen.next();就很容易理解了。
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function() { var self = this, args = arguments; return new Promise(function(resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } const middleware1 = /*#__PURE__*/ (function() { var _ref = _asyncToGenerator(function*(ctx, next) { console.log(1); yield next(); console.log(6); }); return function middleware1(_x, _x2) { return _ref.apply(this, arguments); }; })(); const middleware2 = /*#__PURE__*/ (function() { var _ref2 = _asyncToGenerator(function*(ctx, next) { console.log(2); yield next(); console.log(5); }); return function middleware2(_x3, _x4) { return _ref2.apply(this, arguments); }; })(); const middleware3 = /*#__PURE__*/ (function() { var _ref3 = _asyncToGenerator(function*(ctx, next) { console.log(3); yield next(); console.log(4); }); return function middleware3(_x5, _x6) { return _ref3.apply(this, arguments); }; })(); Promise.resolve( middleware1( context, /*#__PURE__*/ _asyncToGenerator(function*() { return Promise.resolve( middleware2( context, /*#__PURE__*/ _asyncToGenerator(function*() { return Promise.resolve( middleware3( context, /*#__PURE__*/ _asyncToGenerator(function*() { return Promise.resolve(); }) ) ); }) ) ); }) ) ).then(() => { console.log("end"); });
参考连接:
https://chenshenhai.github.io...
https://segmentfault.com/a/11...