阅读connect&express&socket.io源码遇到的问题
发布于 10 个月前 作者 mtaa 1032 次浏览 最后一次编辑是 10 个月前 来自 问答
// 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

setImmediateprocess.nextTick的区别我能搞清楚,这里我的问题是为什么这里要异步调用?直接done(err)有什么不好的地方吗?我试过注释这句话直接调用,重跑了测试用例也没发现问题。

下面是链接和相关代码:

#L151

/* 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();
};

3 回复

用户在某个中间件里调用 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 请求』,我就明白了,谢谢。

中间件遍历方式优化 easy-websocket

这文章学习了,最近也在写基于websocket,类似connect的中间件框架,还不完善,有问题可以再联系你吗? jsonrpc.io

==== 我又去找资料发现这篇分析setImmediate,process.nextTick的文章居然是我自己写的,是以前帐号写的。 https://cnodejs.org/topic/5556efce7cabb7b45ee6bcac

!!!- -

@mtaa 加油,有问题随时 @ 我,https://github.com/vincentLiuxiang/easy-websocket 这个库是我自己基于 node.js 实现的 websocket, 代码比较精简,适合深入了解 websocket 内部原理

回到顶部