关于nextTick的一点问题

1,026 阅读2分钟

最近几天在拿node搞graphql,用了dataloader来解决n+1问题。写loader的时候把loader放到了全局,当时心里就烦嘀咕,要是两个请求一起打过来,dataloader返回的数据会不会混淆? 比如以下这种情况:

    const pageLoader = new DataLoader(async (ids: string[]) => {
       return controller.getPages(ids);
    });

假如两个请求的id有重合,一个ids=[1,2], 另外一个ids=[2,3],如果dataloader把ids合并成[1,2,3]去请求,那返回的结果咋办?

搞数据试了几次,发现dataloader会在第一个请求完成之后再去load第二个请求,并不存在我上面脑补的情况。 那问题又来了,dataloader咋搞的?

DataLoader

翻了下dataloader的源码,在调用loader.load(id)的时候,会做两件事:

  1. 生成一个promise,合着id一起放到queue里。
  2. 如果是第一个key,那还要启一个任务来收集queue里的id
 this._queue.push({ key, resolve, reject });
      // Determine if a dispatch of this queue should be scheduled.
      // A single dispatch should be scheduled per queue at the time when the
      // queue changes from "empty" to "full".
      if (this._queue.length === 1) {
        if (shouldBatch) {
          // If batching, schedule a task to dispatch the queue.
          enqueuePostPromiseJob(() => dispatchQueue(this));
        } else {
          // Otherwise dispatch the (queue of one) immediately.
          dispatchQueue(this);
        }
      }
      
var enqueuePostPromiseJob =
  typeof process === 'object' && typeof process.nextTick === 'function' ?
    function (fn) {
      if (!resolvedPromise) {
        resolvedPromise = Promise.resolve();
      }
      resolvedPromise.then(() => process.nextTick(fn));  // 关键, 任务被放到nextTick来执行了
    } :
    setImmediate || setTimeout;

可以看到,收集queue里id的任务被放到nextTick里面来执行了。 也就是说,在nextTick回调执行之前load的id会被放到queue里作为一个id集合来处理,而node在处理下一个请求之前,就调用了nextTick的回调。所以 两个请求的id是不会混合的。。

但问题又来了,两个request的回调函数应当是放在事件循环的Poll Phase里面的,在处理完这个phase的队列之前,nextTick为什么会被调用?

nextTick

翻了n多篇文章,大概讲的都是node维护了一个nextTickQueue,libuv在每个phase即将结束,要进入下个phase之前,会检查nextTickQueue里的回调并执行。

然后看到了这篇文章,讲node 11.0之后, 为了和浏览器一致,在macrotask执行完之后都会去执行microtask。 这个理论说得通,但问题是我用的是node 8,所以应该不是因为这个原因。

最后去提了一个issue,大神给的解答大概就是说nexttick和eventloop压根儿不搭嘎,eventloop每次跑完一个handler都会跑nexttick。 大概就这样吧,也不想深究了,毕竟node 11以后,感觉已经完全可以把nexttick当作一个microtask来看了。

参考文献

You really hate NodeJS! Here’s why…