为什么这个node.js回调不会立即运行?

时间:2015-02-23 17:45:48

标签: node.js express

使用express-generator它会吐出一些错误处理代码:

app.use('/', routes);
app.use('/users', users);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

在这个例子中,如果(无论出于何种原因)我的路线被破坏或找不到路线或其他什么,代码会回退到第二个代码块中抛出404。为什么在第二个代码块(404处理程序)开始执行后,第三个代码块(500处理程序)不会立即执行?

我认为node.js回调的工作方式是函数开始执行并继续在后台执行,然后下一个回调开始同时执行。但显然我对同步回调的工作原理有点困惑。以上代码是否以某种方式知道"等待"直到404处理程序代码在运行500错误处理程序之前完成执行?

2 个答案:

答案 0 :(得分:1)

初始化应用时,所有app.use()语句都会运行。他们每个人都建立了一个中间件"处理程序。他们当时并没有真正运行处理程序,只是将它们注册到Express中间件堆栈中。如果它们之前没有处理程序处理页面,那么这两个最后app.use()中间件处理程序将按顺序获取请求,而第二个仅在第一个将请求传递给更多处理程序时才能看到请求。

404处理程序将状态设置为404,然后调用中间件堆栈中的next()处理程序。这最终将成为您上次app.use()语句,该语句将查看状态是否已设置,如果未设置,则会将其设置为500,但如果之前设置为404,它将留在那。然后,它将为缺少的页面应用默认呈现,以显示页面中的状态。

这是一种具有应用默认渲染的单个位置的方法,但是可以设置错误的多个位置。

这些都与异步行为无关。仅当先前的请求处理程序调用{​​{1}}时,才会启动列表中的下一个请求处理程序。没有"等待"。您可以使用最后next()语句来考虑404请求处理程序,就像调用app.use()时的同步函数调用一样,它只是说请立即执行链中的下一个请求处理程序(它碰巧知道的是为错误状态代码提供默认呈现的那个。)


查看next()在Express中的工作方式可能会有所帮助。

每次调用app.use()都会向列表添加请求处理程序。当给定的http请求进入时,Express从列表中的第一个请求处理程序开始,并检查列表中第一个请求处理程序的参数是否与当前请求匹配(例如,路径是否匹配或者是否设置了任何其他参数) app.use()声明)。如果匹配,则调用该请求处理程序。如果该请求处理程序没有调用app.use()让列表中的下一个请求处理程序有机会处理该请求,则完成所有处理,并且Express假定第一个请求处理程序已完全处理该请求。如果第一个请求处理程序没有完全处理请求(假设它只是检查标题中的cookie值并希望处理继续到其他处理程序),那么它将调用next()。这告诉express要查看列表中的下一个next()处理程序,看看它是否与此请求匹配。

只要没有请求处理程序与当前请求匹配,或者每个请求处理程序都保持调用app.use()以保持链路继续运行,Express将继续沿着列表行进,寻找一些请求处理程序来处理请求并生成服务器响应。在您的特定示例中,链中的第二个到最后一个请求是next()处理程序。它假设如果Express在链中这么远,那么没有处理程序处理了这个请求,因此它必须是对该服务器不能处理的页面的请求。因此,它将状态设置为404.然后,因为错误页面的默认呈现位于最后一个请求处理程序中,它调用404以触发最后一个默认页面呈现,其中包含错误。


答案 1 :(得分:0)

解释器中只有一个线程正在运行您的代码。 I / O操作同时执行,以便JS执行可以继续而不会阻塞I / O.它被称为异步,因为回调执行的时间和顺序不在您的直接控制之下。两个JavaScript函数不会同时执行。

上面的代码将在没有执行回调函数的 的情况下完全运行。代码运行后,http模块将侦听客户端请求(通常,您没有在上面显示)。回调将根据需要执行以响应这些客户端请求。它们不是一直在单独的线程中运行并等待数据。 app.use只是在express中间件堆栈中注册函数。当请求进入与您指定的路由匹配(或不匹配)时,将按顺序调用适用的回调。这就是你必须在中间件中调用next的原因;如果不这样做,则停止处理该请求对象(此设计称为continuation passing style)。

执行这些函数的完全顺序对您来说并不为人所知,并不重要。只有 relative 顺序很重要,即首先调用两个函数中的哪一个。通常,代码结构将保证这一点(即向I / O调用提供回调函数)。这意味着解释器能够立即处理每个I / O活动的结果,而不必担心线程管理等。