Node中process.nextTick(),定时器和setImmediate()

2,694 阅读2分钟

最近在学习nodejs,看到了异步这块,就整理了下Node中几个异步API

定时器:

setTimeout和setInterval与浏览器中的API是一致的,分别用于单次和多次定时执行任务。他们的实现原理和异步I/O比较类似,只是不需要I/O线程池的参与。调用他们创建的定时器会被插入到定时器观察者内部的一个红黑树中。每次Tick执行的时候,会从该红黑树中迭代取出定时器对象,检查是否超过定时时间,如果超过,就形成一个事件,它的回调函数将立即执行。

问题:并非精确,尽管事件循环非常快,但是如果某一次循环占用的时间比较多,那么下次循环时,它也许已经超时很久了。

process.nextTick()

很多人会为了立即异步执行一个任务,会这样调用setTimeout()来达到所需的效果。

setTimeout(function () {
    // TODO
}, 0)

由于事件循环的特点,定时器的精确度不够。而事实上,采用定时器需要动用红黑树,创建定时器对象和迭代等操作,而setTimeout(fn, 0)较为浪费性能。而process.nextTick()方法的操作相对较为轻量,具体代码如下:

process.nextTick = function (callback) {
    if( process._exiting ) return;
    if( tickDepth >= process.maxTickDepth ){
        maxTickWarn();
    }
    var tock = { callback: callback};
    if( process.domain ) tock.domain = process.domain;
    nextTickQueue.push( tock );
    if( nextTickQueue.length ) {
        process._needTickCallback();
    }
}

每次调用process.nextTick()方法,只会将回调函数放入队列中,在下一轮Tick时取出执行。定时器中采用红黑树的操作时间复杂度为O(lg(n)), nextTick()的时间复杂度为O(1)。相对之下,process.nextTick()更加有效。

setImmediate()

setImmdiate()方法与process.nextTick()方法类似,都是将回调函数延迟执行。

process.nextTick( function () {
    console.log("延迟执行!");
});
console.log("正常执行!");

// 结果:
// 正常执行
// 延迟执行

用setImmediate()实现:

setImmediate( function () {
    console.log("延迟执行")
});
console.log("正常执行");

// 结果:
// 正常执行
// 延迟执行

放在一起执行:

proccess.nextTick( function () {
    console.log("nextTick");
});
setImmediate( function () {
    console.log("immediate");
});
console.log("正常执行");


// 结果:
// 正常执行
// nextTick
// immediate

process.nextTick()中的回调函数优先级高于setImmediate(). process.nextTick()属于idle观察者,setImmediate()属于check观察者。在每一个轮循环检查中,idle观察者先于I/O观察者,I/O观察者先于check观察者。

三者的执行顺序为process.next() 先于 定时器 先于 setImmediate()