process.nextTick(callback[, ...args])


  • callback <Function>
  • ...args <any> 当调用 callback 时传入的其他参数。

process.nextTick() 方法将 callback 添加到下一个时间点的队列。 一旦当轮的事件循环全部完成,则调用下一个时间点的队列中的所有回调。

这不是 setTimeout(fn, 0) 的简单别名。 它的效率更高。 它会在事件循环的下一个时间点中触发任何其他 I/O 事件(包括定时器)之前运行。

console.log('开始');
process.nextTick(() => {
  console.log('下一个时间点的回调');
});
console.log('调度');
// 输出:
// 开始
// 调度
// 下一个时间点的回调

这在开发 API 时非常重要,以便在构造对象之后但在发生任何 I/O 之前,为用户提供分配事件处理函数的机会:

function MyThing(options) {
  this.setupOptions(options);

  process.nextTick(() => {
    this.startDoingStuff();
  });
}

const thing = new MyThing();
thing.getReadyForStuff();

// thing.startDoingStuff() 现在被调用,而不是在之前。

对于 100% 同步或 100% 异步的 API,此方法也非常重要。 考虑如下示例:

// 警告!不要这样使用!这是不安全的!
function maybeSync(arg, cb) {
  if (arg) {
    cb();
    return;
  }

  fs.stat('file', cb);
}

此 API 是不安全的,因为在以下情况中:

const maybeTrue = Math.random() > 0.5;

maybeSync(maybeTrue, () => {
  foo();
});

bar();

不清楚是否先调用 foo()bar()

以下方法则更好:

function definitelyAsync(arg, cb) {
  if (arg) {
    process.nextTick(cb);
    return;
  }

  fs.stat('file', cb);
}

在处理其他 I/O 之前,下一个时间点的队列在事件循环的每次传递中完全耗尽。 因此,递归地设置 nextTick() 回调将阻塞任何 I/O 的发生,就像一个 while(true); 循环。

  • callback <Function>
  • ...args <any> Additional arguments to pass when invoking the callback

The process.nextTick() method adds the callback to the "next tick queue". Once the current turn of the event loop turn runs to completion, all callbacks currently in the next tick queue will be called.

This is not a simple alias to setTimeout(fn, 0). It is much more efficient. It runs before any additional I/O events (including timers) fire in subsequent ticks of the event loop.

console.log('start');
process.nextTick(() => {
  console.log('nextTick callback');
});
console.log('scheduled');
// Output:
// start
// scheduled
// nextTick callback

This is important when developing APIs in order to give users the opportunity to assign event handlers after an object has been constructed but before any I/O has occurred:

function MyThing(options) {
  this.setupOptions(options);

  process.nextTick(() => {
    this.startDoingStuff();
  });
}

const thing = new MyThing();
thing.getReadyForStuff();

// thing.startDoingStuff() gets called now, not before.

It is very important for APIs to be either 100% synchronous or 100% asynchronous. Consider this example:

// WARNING!  DO NOT USE!  BAD UNSAFE HAZARD!
function maybeSync(arg, cb) {
  if (arg) {
    cb();
    return;
  }

  fs.stat('file', cb);
}

This API is hazardous because in the following case:

const maybeTrue = Math.random() > 0.5;

maybeSync(maybeTrue, () => {
  foo();
});

bar();

It is not clear whether foo() or bar() will be called first.

The following approach is much better:

function definitelyAsync(arg, cb) {
  if (arg) {
    process.nextTick(cb);
    return;
  }

  fs.stat('file', cb);
}

The next tick queue is completely drained on each pass of the event loop before additional I/O is processed. As a result, recursively setting nextTick() callbacks will block any I/O from happening, just like a while(true); loop.