koa2中间件的调用顺序

找不到合适的描述方式,顺着序号读粗体字,如粗体字描述的结论不明白,再参照其后面的代码片段及代码说明去理解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()的值为下一个中间件函数得异步代码值,那么可以使用asyncawait将代码改写为如下形式:

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中的中间件函数按洋葱模型执行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 本节将结合例子和源码对koa2的中间件机制做一介绍。 什么是中间件? 中间件的本质就是一种在特定场景下使用的函数,...
    空无一码阅读 1,548评论 0 2
  • 我们知道,Koa 中间件是以级联代码(Cascading) 的方式来执行的。类似于回形针的方式,可参照下面这张图:...
    中间件阅读 1,726评论 0 4
  •   koa 中间件是以级联代码(Cascading) 的方式来执行的,可参照下面这张图: callback的源码如...
    小小的开发人员阅读 2,438评论 0 4
  • 框架提出的背景 ES6/7带来的变革 自ES6确定和ES7中async/await开始普及,Node的发展变得更加...
    宫若石阅读 8,749评论 1 14
  • 弄懂js异步 讲异步之前,我们必须掌握一个基础知识-event-loop。 我们知道JavaScript的一大特点...
    DCbryant阅读 2,910评论 0 5

友情链接更多精彩内容