开发express中间件的时候遇到一个问题: 代码如下:
app.use(function(req, res, next){ next(); console.log(“after next”); }); app.use(function(req, res, next){ console.log(“before next”); next(); });
这里的中间件调用结果是:
before next after next
所以我认为中间件是一个栈结构,next前的代码先执行,然后执行next调用下一个中间件,最后回到当前的中间件,执行next后面的代码。 但是我写了另外一个例子,并不支持我这种理解。 代码如下:
app.use(function(req, res, next){ next(); console.log(“after next”) }); app.use(function(req, res, next){ process.nextTick(function(){ console.log(“before next”) next(); }); });
执行结果:
after next before next
请问这是什么原因?
当前next栈结构只是同步执行的,这里的异步的代码要到等到下次nextTick事件执行,只有一个线程,一次只能执行一个事件
理解nextTick的机制你就明白了
next 是同步的 nextTick 是异步的,也就是会等到当前所有同步操作完成之后才有机会轮到它。
@holyselina 中间件的栈结构也是需要调用next才执行的,而我在异步操作中调用next,按道理也是顺序执行中间件,为什么就不对了,这个地方能解释下吗?
@klesh 嗯,我知道nextTick是异步的,但是我的next也是在异步操作中调用的
@keller35 啊? … 首先,想像虚拟机是一个环回系统,js 代码就一个队例,虚拟机不停地从队例中取出“一段同步”代码。执行,完成后又去队例中取出另一段代码,执行。。。直到队列清空,程序就退出了。当然,每次执行又可能产出新的一段“代码”插入到队列中,这样程序就一直地运行下去。
第一个 next 执行后,是去到 nextTick 对吧,但是所有一切在nexTick中的代码这个时候都不会执行,而是被插入到队例后面。然后程序继续往下走,就执行了 console.log(“after next”)。然后本次“同步”代码全部执行完了,虚拟机进入下一次循环,这时 nextTick 中的代码才会被执行。
@klesh 嗯,谢谢你的回答! 你所说的我都明白,event loop我也理解。 但是我不理解的地方是:next作为下一个中间件的调用函数,需要调用了代码才会执行到下一个中间件。而这里是还未调用next的时候,就先执行了console.log(“after next”)?
@keller35 你……,真的明白吗?哎,next不是未调用,事实上第一个next函数绝对是正常调用了,这时候就转到第二个 use ,其中传给nextTick的函数 不会马上执行,process.nextTick只是把这个函数插入 event loop,而不是马上执行这个函数。插完函数就立即返回了,这个时候就回到第一个 use 中执行 console.log(“after next”)。到了这个点,本次 loop 才完成,之后才会把刚才插入的函数拿出来执行,打印出 before next 。
你把next()
理解错了. express的next()
跟koajs的yield next
是不一样的…
app.use(function A(req, res, next){
next();
console.log(“after next”)
});
app.use(function B(req, res, next){
process.nextTick(function(){
console.log(“before next”)
next();
});
});
app.use(function C(req, res, next) {
//
});
// N ∈N+
app.use(function(req, res, next) {
// event loop N: A-start
// event loop N: B-start
process.nextTick(function(){
// event loop N+1:
console.log(“before next”);
// event loop N+1: C-start
// ...
// event loop N+1: C-end
});
// event loop N: B-end
console.log(“after next”);
// event loop N : A-end
})
@William17 嗯,谢谢你的回答。 是我把next理解错了,看了你的第二段代码就明白了。 那可否简单说一下koa的next有什么不同?
// yield next;
// 如果下一个中间件通过yield执行一些异步操作,那下面的console.log将会在异步操作之后才被执行
// next();
// 假如下一中间件有异步代码,下面的console.log总会在异步代码执行之前执行
console.log("after");