Use setImmediate if you want to queue the function behind whatever I/O event callbacks that are already in the event queue. Use process.nextTick to effectively queue the function at the head of the event queue so that it executes immediately after the current function completes.
So in a case where you're trying to break up a long running, CPU-bound job using recursion, you would now want to use setImmediate rather than process.nextTick to queue the next iteration as otherwise any I/O event callbacks wouldn't get the chance to run between iterations.
可以看到 nextTick 的执行优先级是比 setImmediate 高的,I/O event 介于两者之间。
Callbacks passed to process.nextTick will usually be called at the end of the current flow of execution, and are thus approximately as fast as calling a function synchronously. Left unchecked, this would starve the event loop, preventing any I/O from occurring. setImmediates are queued in the order created, and are popped off the queue once per loop iteration. This is different from process.nextTick which will execute process.maxTickDepth queued callbacks per iteration. setImmediate will yield to the event loop after firing a queued callback to make sure I/O is not being starved. –
nextTick 几乎可以是认为是同步的,如果用 nextTick 过于频繁则会导致 event loop 饿死,
I think I can illustrate this quite nicely. Since nextTick is called at the end of the current operation, calling it recursively can end up blocking the event loop from continuing. setImmediate solves this by firing in the check phase of the event loop, allowing event loop to continue normally.
最经典的还是下面这张图
可以看出 nextTick 在每次 event loop 的中间阶段都会执行,也就是说只要有空隙,它就会执行。
const fs = require('fs');
const http = require('http');
const options = {
host: 'www.baidu.com',
port: 80,
path: '/index.html'
};
console.log('Start');
setTimeout(() => console.log('TO1'), 0);
setImmediate(() => console.log('IM1'));
process.nextTick(() => console.log('NT1'));
setImmediate(() => console.log('IM2'));
process.nextTick(() => console.log('NT2'));
http.get(options, () => console.log('IO1'));
fs.readdir(process.cwd(), () => console.log('IO2'));
setImmediate(() => console.log('IM3'));
process.nextTick(() => console.log('NT3'));
setImmediate(() => console.log('IM4'));
fs.readdir(process.cwd(), () => console.log('IO3'));
console.log('Done');
/*
打印以下内容
Start
Done
NT1
NT2
NT3
TO1
IO2
IO3
IM1
IM2
IM3
IM4
IO1
对应的其实就是下面这幅官网给出的图的顺序
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
*/
关于 nodejs 中 event loop 的最好的文章应该就是这一篇
这篇文章中说 check 阶段就是检查 setImmediate 的,感觉是对的,但是有待考证。
还有一些待考证的:据说在 w3c 规范中没有 macrotask 和 microtask 这两概念了,改成了 task 和 job 了。也就是说实际上 macrotask === task,而 microtask === job。