slackkoala 2019-06-27
async await 的特点,形成了一个洋葱式的流程,和 JS 的事件流 (捕获 -> target -> 冒泡) 相似handleRequest(ctx, fnMiddleware) {
const res = ctx.res;
res.statusCode = 404;
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx);
onFinished(res, onerror);
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}上述代码是 request 事件的句柄,也就是说每一个请求到来,都会执行这个总方法res.end(body)fnMiddleware(ctx) 就是执行所有中间件函数,然后返回一个 Promise 对象,不出错的话执行 handleResponsefunction (context, next) {
// last called middleware #
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
// 返回给 next()
if (!fn) return Promise.resolve()
try {
// 返回给 next(),最外一层返回给 fnMiddleware(ctx).then(handleResponse)
return Promise.resolve(fn(context, function next () {
// 返回给外一层 fn 的 await
return dispatch(i + 1)
}))
} catch (err) {
return Promise.reject(err)
}
}
}fnMiddleware(ctx).then(handleResponse))index,是记录执行过的中间件数量。一旦有序号大于数量,说明有中间件执行了两次 await next,这是不被允许的function dispatch(0){ // 第一层的序号
return Promise.resolve(async function a0(){
cnosole.log('0-0')
await 111(function next0(){
return (function dispatch(1){ // 第二层的序号
return Promise.resolve(async function a1(){
cnosole.log('1-0')
await 222(function next1(){
return (function dispatch(2){ // 第三层的序号
return Promise.resolve(async function a2(){
cnosole.log('2-0')
await 333(function next2(){
return (function dispatch(3){ // i == middleware.length ,算是洋葱芯吧
// fn[3] == undefined,说明中间件已经到洋葱的最里面了,开始向外返回
return Promise.resolve()
})()
})()333
console.log('2-1')
})
})()
})()222
console.log('1-1')
})
})()
})()111
console.log('0-1')
})
}
dispatch(0).then(handleResponse)app.use(async function (ctx,next) {
console.log('1-1')
await new Promise(function(resolve, reject){
setTimeout(function () {
console.info
("wait for 10 mini seconds.");
resolve();
},10);
});
console.log('1-2')
next();
console.log('1-3')
})
app.use(async function (ctx,next) {
console.log('2-1')
await new Promise(function(resolve){
setTimeout(function () {
console.info
("wait for 10 mini seconds");
resolve();
},10);
});
console.log('2-2')
next();
console.log('2-3')
})试试 next() 前面加上 await 和不加 await 的区别就明白了res、req 对象的封装。其他的功能都由外部中间件提供。代码不是很多,但是很精妙,对于代码能力的提高有不小的帮助