// all done
if (!layer) {
defer(done, err); // ????? 为什么这里要用defer异步调用回调函数done?
return;
}
上面代码是connect.js中handle函数的代码,layer中间件运行完之后由defer
异步调用最后的回调函数done
。
express.js和socket.io中也是类似这样调用的,区别是express.js直接用的setImmediate
,socket.io里用的process.nextTick
。
在connect.js的代码历史中,只注释了这么一句:
Correctly invoke async callback asynchronously
setImmediate
和process.nextTick
的区别我能搞清楚,这里我的问题是为什么这里要异步调用?直接done(err)
有什么不好的地方吗?我试过注释这句话直接调用,重跑了测试用例也没发现问题。
下面是链接和相关代码:
/* istanbul ignore next */
var defer = typeof setImmediate === 'function'
? setImmediate
: function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
/**
* Handle server requests, punting them down
* the middleware stack.
*
* @private
*/
proto.handle = function handle(req, res, out) {
var index = 0;
var stack = this.stack;
// final function handler
var done = out || finalhandler(req, res, {
env: env,
onerror: logerror
});
function next(err) {
// next callback
var layer = stack[index++];
// all done
if (!layer) {
defer(done, err); // ????? 为什么这里要用defer异步调用done函数?
return;
}
// route data
var path = parseUrl(req).pathname || '/';
var route = layer.route;
// call the layer handle
call(layer.handle, route, err, req, res, next);
}
next();
};
用户在某个中间件里调用 next() ,而 next 执行后又没有能与 url 规则匹配上的中间件时,done(err) 才会执行, 比如:
var app = connect();
app.use('/api', (req, res, next) => {
console.log('hello world');
next();
});
app.use('/other/api', () => {
console.log('no invoke here !!!');
})
当你请求 url 为 /api/xxx 时,第一个中间件会执行,在第一个中间件里面调用 next() 后,
connect 的 中间件 stack 剩余的数组里并没有 与 /api/test 能匹配上的中间件,此时 done(err)
会执行,
done 的作用是在调用 next 后,后面没有任何能匹配上的中间件时,connect帮你响应一个 404 Not Found.
这里当然可以不需要调用 setImmediate 或 process.nextTick,
但是代价是:
任何匹配不上中间件路由规则的 http 请求,connect 都是会即时响应404,而不会及时让出 cpu 时间来让 connect 来优先处理其他请求。 setImmediate / process.nextTick 可以让这次匹配不上中间件的请求 (业务层面你可以认为是一个无效请求),响应时机推后,而让 node 主线程快速处理新的 http 请求。
另外你真的懂 setImmediate / process.nextTick 吗? 可以参考这篇文章:https://github.com/vincentLiuxiang/mybook
@vincentLiuxiang 谢谢,回复晚了,早先看到忘记回复了。我当时想不明白为啥要用defer,不知道有啥好处,你一说『让 node 主线程快速处理新的 http 请求』,我就明白了,谢谢。
这文章学习了,最近也在写基于websocket,类似connect的中间件框架,还不完善,有问题可以再联系你吗? jsonrpc.io
==== 我又去找资料发现这篇分析setImmediate,process.nextTick的文章居然是我自己写的,是以前帐号写的。 https://cnodejs.org/topic/5556efce7cabb7b45ee6bcac
!!!- -
@mtaa 加油,有问题随时 @ 我,https://github.com/vincentLiuxiang/easy-websocket 这个库是我自己基于 node.js 实现的 websocket, 代码比较精简,适合深入了解 websocket 内部原理