找不到合适的描述方式,顺着序号读粗体字,如粗体字描述的结论不明白,再参照其后面的代码片段及代码说明去理解koa2中间件的调用顺序。
koa2中的中间件使用如下:
app.use((ctx,next)=>{
next();
});
app.use((ctx,next)=>{
next();
});
...
1 next()相当于对下一个中间件函数进行调用
而且next()的值相当于下一个中间件函数的返回值
app.use((ctx,next)=>{
let testStr = next();
conslole.log(testStr);//Promise { undefined }
});
app.use(middleware2);
...
function middleware2(ctx,next){
next();
return "test";
}
这段代码的运行结果大体相当于如下代码片段:
app.use((ctx,next)=>{
let testStr = middleware2(ctx,next);
conslole.log(testStr);//undefined
});
...
function middleware2(ctx,next){
next();
return "test";
}
2 koa2默认对中间件函数进行了promise封装。
app.use((ctx,next)=>{
var result = middleware2(ctx,next);
console.log(result);//undefined
});
...
function middleware2(ctx,next){
next();
}
运行打印结果为undefined
改写代码为:
app.use((ctx,next)=>{
var result = next();
console.log(result);//Promise { undefined }
});
app.use((ctx,next)=>{
next();
});
...
运行打印结果为Promise { undefined }
,可见koa2默认对中间件函数进行了promise封装。
3 上一个中间件的next()可以拿到下一个中间件的返回值,并封装进返回的promise对象中
以下代码:
app.use((ctx,next)=>{
var result = next();
console.log(result);//Promise { "testStr" }
});
app.use((ctx,next)=>{
next();
return "testStr";
});
...
运行打印结果为Promise { "testStr" }
4 和普通的js函数调用一样,上一个中间中的next()对下一个中间件函数调用时,不会等待下一个中间件中异步代码。
以下代码:
app.use((ctx,next)=>{
var result = next();
console.log(result);//Promise { 0 }
});
app.use((ctx,next)=>{
next();
let num = 0;
setTimeout(()=>{
num = 3;
},1000);
return num;
});
...
运行打印结果为Promise { 0 }
以下代码:
app.use((ctx,next)=>{
var result = next();
console.log(result);//Promise { <pending> }
});
app.use((ctx,next)=>{
next();
return promiseFn();
});
...
function promiseFn(){
return new Promise(function(resovle,reject){
setTimeout(function(){
resovle('5');
},5000)
});
}
运行打印结果为Promise { <pending> }
,pending为promise的进行中状态,意思就是并不等待promise的状态改变为resolved或fulfilled而立即返回一个“进行中”状态
如想要等待异步函数的返回结果,使next()的值为下一个中间件函数得异步代码值,那么可以使用async
和await
将代码改写为如下形式:
app.use(async(ctx,next)=>{
var result = await next();
console.log(result);//5
});
app.use((ctx,next)=>{
next();
return promiseFn();
});
...
function promiseFn(){
return new Promise(function(resovle,reject){
setTimeout(function(){
resovle('5');
},5000)
});
}
如此就能拿到下一个中间件函数中的异步代码返回值了,输出结果为5
await关键字的意思是阻塞代码并对其后的表达式或函数调用进行取值
使用await关键字的话函数必须是async函数
但为了保证中间件的执行顺序,koa2规定如果某个中间件使用了await关键字,那么每一个中间件的next前面都应添加await关键字
6 中间件函数的执行顺序以next()为分隔呈洋葱型
app.use((ctx,next)=>{
console.log('111');
next();
console.log('222');
});
app.use((ctx,next)=>{
console.log('333');
next();
console.log('444');
});
...
输出顺序为:
//111
//333
//444
//222
因为next()相当于对下一个中间件函数的调用,以上代码相当于
console.log('111');
test(ctx,next);
console.log('222');
function test(ctx,next){
console.log('333');
next();
console.log('444');
}
输出顺序肯定为:
//111
//333
//444
//222
当仅在第二个中间件中使用await关键字时有可能破坏这个执行顺序,
app.use((ctx,next)=>{
console.log('111');
var result = next();
console.log(result);//Promise { <pending> }
console.log('222');
});
app.use(async(ctx,next)=>{
console.log('333');
next();
let testVar = await promiseFn();
console.log('444');
});
...
function promiseFn(){
return new Promise(function(resovle,reject){
setTimeout(function(){
resovle('5');
},500)
});
}
期待输出结果为:
//111
//333
//444
//222
输出结果却变为:
//111
//333
//222
//444
因为中间件1中的next()不会等待下一个中间件函数中的异步代码(即使阻塞)拿到Promise { <pending> }
结果后继续往下执行打印222
,然后等阻塞代码执行完后才打印444
。造成中间件函数不再以next()为分隔呈洋葱模型执行,所以每一个next()前都必须添加await关键字。
app.use(async (ctx,next)=>{
console.log('111');
var result = await next();
//console.log(result);//undefined
console.log('222');
});
app.use(async(ctx,next)=>{
console.log('333');
await next();
let testVar = await promiseFn();
console.log('444');
});
...
function promiseFn(){
return new Promise(function(resovle,reject){
setTimeout(function(){
resovle('5');
},500)
});
}
输出结果仍为:
//111
//333
//444
//222
如此就保证了koa2中的中间件函数按洋葱模型执行。
网友评论