express中间件的问题
发布于 8 年前 作者 keller35 6562 次浏览 来自 问答

开发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

请问这是什么原因?

11 回复

当前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");
回到顶部