在node中,大部分介绍的都是异步的I/O,但是在Node中还存在一种与I/O无关的异步API,这些其实也值得我们去关注,它们分别是setTimeout()、setInterval()、process.nextTick()和setImmediate()
**
一、定时器
setTimeout()和setInterval()分别用于单次和多次定时执行任务。调用setTimeout()或者setInterval()创建的定时器会被插入到定时器观察者内部的一个红黑树上。每次Tick执行的时候,会从红黑树上迭代取出定时器对象,检查时间是否超过定时时间,若超过就会触发回掉函数理解执行。这里我们以setTimeout为例
setTimeout的行为
定时器的问题在于它并非精确的。尽管事件循环很快,但当某一次的循环占用的时间比较多,那么下次进入循环可能就会超时了。例如setTimeout()设定一个任务在10ms种后执行,但是9ms后有一个任务占用了5ms的cpu时间片,再次轮到定时器执行的时候,就会超时4ms.
二、process.nextTick()
有时候我们可能需要创建一个立即异步执行的任务,使用定时器,我们可以写成这样来达到效果
setTimeout(function(){
//to do
}, 0)
这里将时间设置为0,但是上面讲过由于事件循环的特点,定时器的精度不够。而且定时器需要动用红黑树,创建定时器对象和迭代等操作,setTimeout(fn, 0)就比较浪费性能。而process.nextTick()方法的操作就很轻量。
每次调用process.nextTick()方法,就会将回调函数放到队列种,再下一轮的Tick时取出执行。定时器种采用红黑树的操作时间复杂度0(log(n)),nextTick()时间复杂度为O(1)。很明显process.nextTick()更加高效。
process.nextTick(() => {
// to do
})
三、setImmediate()
setImmediate()和process.nextTick()的方法很类似,都是将回调函数延迟执行。但是两者还是有区别的
setImmediate(() => {
console.log('setImmediate 延迟执行')
})
process.nextTick(() => {
console.log('nextTick 延迟执行')
})
console.log('正常执行')
执行结果如下
从结果种我们知道,process.nextTick()中的回调函数执行的优先级高于setImmdiate,这里的原因是事件循环对观察者检查有先后的顺序,process.nextTick()属于idle观察者,setImmediate()属于check观察者。在每一轮循环检查中,idle观察者是先于I/O观察者,I/O观察者先于check观察者。
process.nextTick(() => {
console.log('nextTick 延迟执行1')
})
process.nextTick(() => {
console.log('nextTick 延迟执行2')
})
setImmediate(() => {
console.log('setImmediate 延迟执行1')
process.nextTick(() => {
console.log('nextTick 延迟执行3')
})
})
setImmediate(() => {
console.log('setImmediate 延迟执行2')
})
console.log('正常执行')
执行结果如下