最近幾天在拿node搞graphql,用了dataloader來解決n+1問題。寫loader的時候把loader放到了全局,當時內心就煩嘀咕,要是兩個請求一塊兒打過來,dataloader返回的數據會不會混淆? 好比如下這種狀況:node
const pageLoader = new DataLoader(async (ids: string[]) => {
return controller.getPages(ids);
});
複製代碼
假如兩個請求的id有重合,一個ids=[1,2], 另一個ids=[2,3],若是dataloader把ids合併成[1,2,3]去請求,那返回的結果咋辦?git
搞數據試了幾回,發現dataloader會在第一個請求完成以後再去load第二個請求,並不存在我上面腦補的狀況。 那問題又來了,dataloader咋搞的?github
翻了下dataloader的源碼,在調用loader.load(id)的時候,會作兩件事:promise
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爲何會被調用?bash
翻了n多篇文章,大概講的都是node維護了一個nextTickQueue,libuv在每一個phase即將結束,要進入下個phase以前,會檢查nextTickQueue裏的回調並執行。async
而後看到了這篇文章,講node 11.0以後, 爲了和瀏覽器一致,在macrotask執行完以後都會去執行microtask。 這個理論說得通,但問題是我用的是node 8,因此應該不是由於這個緣由。ide
最後去提了一個issue,大神給的解答大概就是說nexttick和eventloop壓根兒不搭嘎,eventloop每次跑完一個handler都會跑nexttick。 大概就這樣吧,也不想深究了,畢竟node 11之後,感受已經徹底能夠把nexttick看成一個microtask來看了。函數