Why does this node.js callback not run immediately?

淺唱寂寞╮ 提交于 2020-01-16 04:18:33

问题


Using the express-generator it spits out some error handling code like this:

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
        });
    });
}

In this example, if (for whatever reason) my routes are broken or the route isn't found or whatever, the code falls back to throw a 404 in the second code block. How come the 3rd code block (the 500 handler) doesn't execute immediately after the 2nd code block (the 404 handler) begins executing?

I thought that the way node.js callbacks work is that the function begins executing and continues to be executed in the background and then the next callback begins executing at the same time. But apparently I am a bit confused by how the synchronous callbacks work. Does the above code somehow know to "wait" until the 404 handler code is done executing before running the 500 error handler?


回答1:


All of the app.use() statements run when your app is initialized. They each set up a "middleware" handler. They don't actually run the handlers at that time, they just register them into the Express middleware stack. If no handler before them handles a page, then these two last app.use() middleware handlers will get a shot at the request in order and the second one only gets to see the request if the first passes the request on to more handlers.

The 404 handler will set the status to 404 and will then call the next() handler in the middleware stack. That will end up being your last app.use() statement which will see if a status has already been set and if not, will set it to 500, but if it was previously set to 404, it will leave it at that. It will then apply a default rendering for a missing page that shows the status in the page.

This is a means of having a single place where the default rendering is applied, but multiple places that could set errors.

None of this really has anything to do with asynchronous behavior. The next request handler in the list is started only when next() is called by an earlier request handler. There is no "waiting". You can think of the 404 request handler using the last app.use() statement like a synchronous function call when it calls next() it is just saying please execute the next request handler in the chain right now (which it happens to know is the one that provides default rendering for the error status code).


It might be helpful to review how app.use() works in Express.

Each call to app.use() adds a request handler to a list. When a given http request comes in, Express starts with the first request handler in the list and checks to see if the parameters of that first request handler in the list match the current request (e.g. does the path match or any other parameters set in the app.use() statement). If it matches, then it calls that request handler. If that request handler does not call next() to let the next request handler in the list have a chance at the request, then all processing is done and Express assumes that the first request handler has completely handled the request. If this first request handler has not completely handled the request (say it was just checking a cookie value in the header and wants processing to continue to other handlers), then it will call next(). This tells express to look at the next app.use() handler in the list and see if it is a match for this request.

As long as no request handler matches the current request or each one that does keeps calling next() to keep the chain going, Express will keep marching down the list looking for some request handler to handle the request and generate a server response. In your specific example, the second to the last request in the chain is a 404 handler. It assumes that if Express got this far down the chain, then no handler has yet handled this request so it must be a request for a page that this server is not designed to handle. Thus, it sets the status to 404. And, then because the default rendering for an error page is in the very last request handler, it calls next() in order to trigger that last default page rendering with the error in it.





回答2:


Only one thread in the interpreter is running your code. I/O operations are performed concurrently so that JS execution can continue without blocking on I/O. It's called asynchronous because the timing and sequence of callback execution is not under your direct control. Two JavaScript functions do not execute simultaneously.

Your code above will run fully without either of the callback functions executing. After your code runs the http module will listen for client requests (typically, you didn't show this above). The callbacks will execute as needed in response to those client requests. They are not running all the time in separate threads and waiting for data. app.use just registers the functions within the express middleware stack. When requests come in that match the routes (or not) that you specify, the applicable callbacks are called in order. That is why you must call next within your middleware; if you don't, processing of that request object stops (this design is called continuation passing style).

The exact order in which these functions are executed is not known to you, and it's not important. Only the relative order matters, i.e. which of two functions will be called first. Usually the code structure will guarantee this (i.e. supplying a callback function to an I/O call). This means the interpreter is able to process the result of each I/O activity immediately without you having to worry about thread management, etc.



来源:https://stackoverflow.com/questions/28679983/why-does-this-node-js-callback-not-run-immediately

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!