定时器
setTimeout和setInterval创建的定时器,会被插入到定时器观察者的一个红黑树上。每次Tick执行的时候,会从该红黑树中取到对应的定时器,检查是否超过定时时间。如果超过了,就会将对应的回调函数放在当前的Tick中,并且执行。
Process.nextTick 和 setImmediate
如果我们想让一段代码异步执行,常见的做法是:
setTimeout(() => {
console.log(123);
}, 0);
但是由于定时器的不准确,Tick执行一次的时间过长,或者从红黑树中拿到定时器的时间,都会导致精度不够准确。
而通过process.nextTick方法调用,只会将回调函数放入队列中(微任务队列),在下一轮Tick时取出执行。定时器采用红黑树的操作时间复杂度为O(lg(n)),而通过process.nextTick的时间复杂度为O(1)。
在执行效果上,setImmediate和nextTick没有明显的差异,但是对应的观察者优先级是
process.nextTick > 异步I/O > setImmediate
这里注意的是,虽然异步I/O的观察者优先级会更高,但是读取文件的操作需要时间,所以回调函数的执行不一定会比setImmediate更快。
在具体实现上,process.nextTick是将回调函数保存在一个数组中,每次Tick执行,会将所有的回调函数全部执行。而setImmediate是将回调函数保存在链表上,每次循环执行链表上的一个回调函数。
例如下面的代码,打印结果为1 2 3 4 5 6
process.nextTick(() => {
console.log(2);
})
console.log(1);
setImmediate(() => {
console.log(4)
process.nextTick(() => {
console.log(5);
})
})
process.nextTick(() => {
console.log(3);
})
setImmediate(() => {
console.log(6);
})
这样的设计是为了保证每一轮循环能够比较快的执行结束。