读书笔记(5)--- 非I/O的异步API

80 阅读1分钟

定时器

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);
})

这样的设计是为了保证每一轮循环能够比较快的执行结束。