如何理解koa中的next();函数?

刚接触node没多久,在熟悉koa框架,关于它的next();函数还是无法完全理解,比如下面这段代码片段:

const one = (ctx, next) => {
  console.log('>> one');
  next();
  console.log('<< one');
}

const two = (ctx, next) => {
  console.log('>> two');
  next();
  console.log('<< two');
}

const three = (ctx, next) => {
  console.log('>> three');
  next();
  console.log('<< three');
}

app.use(one);
app.use(two);
app.use(three);

输出结果:

>> one
>> two
>> three
<< three
<< two
<< one

在我的理解中应该这样输出:

>> one
>> two
>> three
<< two
<< one
<< three

看到网上是这样解释的:

1. 最外层的中间件首先执行。
2. 调用next函数,把执行权交给下一个中间件。
3. ...
4. 最内层的中间件最后执行。
5. 执行结束后,把执行权交回上一层的中间件。
6. ...
7. 最外层的中间件收回执行权之后,执行next函数后面的代码。

我理解的是,加载到three中间件时,输出>>three,调用next();,然后把执行权交给上一层中间件。

但事实为什么是直接把three中间件next();之后的代码也执行了!!!

就是这个问题疑问困扰着我,望解答。


再补充一个问题,为什么去掉three中间件中的next();,输出仍然一样,没有next();中间件仍然会冒泡?

阅读 14.1k
5 个回答

其实不难理解,假设你已经接受了上面关于中间件原理的设定。我们直接来看下three的代码。

  1. 步骤一:执行到console.log('>> three');这行代码(这里你理解)
  2. 步骤二:执行到next();这行代码(你也理解)
  3. 步骤三:console.log('<< three');这行代码为什么会执行?楼主的问题在这里。楼主这里是被中间件的概念给弄晕了,抛开中间件的概念,three其实就是个普通的函数,即便next()已经被调用,这里楼主没有看到return是吧,所以console.log('<< three');还是会被执行。

抛开中间件这层迷雾,就一个普通的函数调用。函数调用是什么样的,这里还是什么样。

const three = (ctx, next) => {
  console.log('>> three');
  next();
  console.log('<< three');
}

个人是这么理解的,仅供参考

one = (ctx, next) => {
  console.log('>> one');
   next();----------------> two = (ctx, next) => {
                              console.log('>> two');
                              next();------------------> three = (ctx, next) => {
                                                           console.log('>> three');
                                                           next();--------------------|
                                                           console.log('<< three');<--| 
                                                         }
                              console.log('<< two');
                            }

  console.log('<< one'); 
}      

three内有没有调用next()都不影响冒泡,没有调用next(),就是不会再传递给下一个中间件,此时three可以看作是一个普通的方法

one = (ctx, next) => {
  console.log('>> one');
   next();----------------> two = (ctx, next) => {
                              console.log('>> two');
                              next();------------------> three = (ctx, next) => {
                                                           console.log('>> three');
                                                           console.log('<< three');
                                                         }
                              console.log('<< two');
                            }

  console.log('<< one'); 
}      

递归的思路,因为three的next后面没有了,会直接返回,然后就打印console.log('<< three')

koa做了一个处理,当走到最后一个中间件时,创建了一个空的promise而已,这样调用者不用关心链条不小心终端的问题。

koa是从第一个中间件开始执行,遇到next进入下一个中间件,一直执行到最后一个中间件,在逆序,执行上一个中间件next之后的代码,一直到第一个中间件执行结束才发出响应。
1240

这种执行方式有一个很形象的名字:洋葱圈模型

clipboard.png

每一个中间件就相当于洋葱的一层,请求从最外层进去,然后从最里层出来,每个中间件都会执行两次。
three已经是最里层的中间件了,所以next()只是执行next后的代码,然后一层一层返回

仔细看看这个图,如果是按你说的方式,那就返回到最外层时突然又跳到最里层,肯定是不对的。

总的来说就是两点:
1.洋葱圈模型
2.每个中间件都会执行两次

满足这个的执行方式只能是one->two->three->three->two->one

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏