我们知道,Node 与其他服务端语言不通在于,Node 为 clients 分配很少的线程,而大部分的后端语言支持为一个 client 分配一个线程。但 Node 这么做的时候,开发者们就一定要注意不要 blocking 线程,因为 blocking 线程不仅阻碍一个 client,而是所有的 clients。但 Node 也有其解决办法,也有 Event Loop 的概念,其中还有 process.nextTick()
和 setImmediate()
等异步方法解决 blocking,今天我们就来看一下这两种方法。
setImmediate()
当我们想立即异步执行一段代码,就可以使用 setImmediate()
,但很多同学会问,Node 也有 process.nextTick()
方法,这两种有什么不同吗?
我用优先级来为每个方法进行标注,其中最高优先级的是 process.nextTick()
,其次是 Promise
(microtask queue),最后才是我们今天介绍的 setImmediate()
(macrotask queue)。也就是说,即使是立即执行代码,setImmediate()
也会让步与 process.nextTick()
和Promise
。接下来,我们来分析一下以下代码的执行顺序,该代码来自于 Node 官网。
const baz = () => console.log('baz');
const foo = () => console.log('foo');
const zoo = () => console.log('zoo');
const start = () => {
console.log('start');
setImmediate(baz);
new Promise((resolve, reject) => {
resolve('bar');
}).then(resolve => {
console.log(resolve);
process.nextTick(zoo);
});
process.nextTick(foo);
};
start();
// start foo bar zoo baz
- 首先执行
console.log("start")
。 - 接下来,我们已经记住了
setImmediate
是 macro task,所以可以先暂时放一放。 - 看到
Promise
,我们知道是 micro task,也可以先放一放。 - 最后我们看到
process.nextTick(foo)
,其优先级最高,我们立即执行。
第一轮代码下来,我们执行的是 ‘start’,其次是 ‘foo’。
接下来我们来看被我们暂时放在一边的 setImmediate
和 Promise
。
- 先看
Promise
,其中resolve('bar')
,接下来我们来看 .then 之后的代码块,第一步执行resolve
的数据,也就是 ‘bar’,接下来再次遇到process.nextTick(zoo)
,也就是 ‘zoo’。 - 最后我们回到
setImmediate(baz)
,执行 ‘baz’。
最后我们的输出是 // start foo bar zoo baz
注意的是,在 ES Modules 也就是 mjs
文件里,执行顺序会不一样。
总结
当我们使用 Node 中的 setImmediate
时,一定要留意的是,setImmediate
是属于 macrotask queue
,所以优先级低于 process.nextTick()
和 Promise
(microtask queue)。