What is The Difference Between setTimeout(), setImmediate() and, process.nextTick()?

Posted May 11, 2016 - 4 min read
Topics:  

As we have seen in my previous post, the event loop is the key concept behind JavaScript asynchronous nature which allows executing concurrently many tasks.

We have also seen that executing computation intensive tasks is not recommended as it may block the event loop for a long time and as a result your application may become very slow.

Fortunately, Node.js provides setTimeout(), setImmediate(), and process.nextTick() to allow a long-running computation task to be asynchronously executed (for example iterating over a big array).

Within this post we are going to explore these methods in depth to have a clear view and eliminate any confusion about when and which method is the right one to use.

Event Loop Overview


      ┌─────────────────────────────┐
  ┌───►    Timers events queue      │
  │   │                             │
  │   └───────────────┬─────────────┘
  │                   │
  │                   ├────────────────┐
  │                   │                │    ┌───────────────────────────────────┐
  │   ┌───────────────▼─────────────┐  │    │                                   │
  │   │     IO events queue         │  └────► ┌───────────────────────────────┐ │
  │   │                             │       │ │ process.nextTick events queue │ │
  │   └───────────────┬─────────────┘       │ │                               │ │
  │                   │                     │ └─────────────┬─────────────────┘ │
  │                   ├─────────────────────►               │                   │
  │                   │                     │ ┌─────────────▼─────────────────┐ │
  │   ┌───────────────▼─────────────┐  ┌────► │     Promise events queue      │ │
  │   │   Immediate events queue    │  │    │ │                               │ │
  │   │                             │  │ ┌──► └───────────────────────────────┘ │
  │   └───────────────┬─────────────┘  │ │  │                                   │
  │                   │                │ │  └───────────────────────────────────┘
  │                   ├────────────────┘ │
  │                   │                  │
  │   ┌───────────────▼─────────────┐    │
  │   │ close handlers events queue │    │
  │   │                             │    │
  │   └───────────────┬─────────────┘    │
  │                   │                  │
  │                   ├──────────────────┘
  │                   │
  └───────────────────┘

See Understanding Node.js Event Loop for more details.

setTimeout()

When you schedule a task (a callback function) to be asynchronously processed using the setTimeout() method, this task will be pushed in the timers queue.

Once the event loop reaches the timers phase, Node.js will check for expired timers. Callbacks of multiple expired timers will be executed in the order in which they were set.

As the event loop may be running other tasks from other event queues, JavaScript does not guaranty that the timers callbacks will be always executed exactly at the specific expiration time.

setImmediate()

The setImmediate() method also allows to run a task asynchronously, but unlike setTimeout() it pushes the task into the immediates queue.

From the diagram above you may notice that Node.js starts processing tasks from the immediates queue just after that tasks from the IO events queue get processed.

The setImmediate() method can be very useful in cases where we want to run an asynchronous task after an IO operation.

import { readFile } from 'fs';

readFile('/etc/passwd', (err, data) => {
  if (err) throw err;
  setImmediate(function myImmediateTask() {
    console.log('This task is from the immediates queue');
  })
});

In the example above, once the IO operation readFile() finishes, Node.js executes its callback. Once the callback is executed we expect that the myImmediateTask() task will be placed in the immediates queue.

During the current iteration of the event loop, when tasks from the IO events queue are processed, the event loop moves to the next phase and start processing tasks from the immediates queue and finaly myImmediateTask() task get executed.

However, if we used setTimeout() instead of setImmediate(), myImmediateTask() would be executed only after the event loop processes tasks from the rest of event queues and starts a new iteration.

The following example shows that myImmediateTask() task is always executed first.

import { readFile } from 'fs';

readFile('/etc/passwd', (err, data) => {
  if (err) throw err;
  setTimeout(function myTimeoutTask() {
    console.log('This task is from the timers queue');
  })
  setImmediate(function myImmediateTask() {
    console.log('This task is from the immediates queue');
  })
});

process.nextTick()

The nextTick events queue is a special queue in the Node.js environment which holds tasks scheduled by process.nextTick().

Before moving to the next event loop phase, Node.js always check the nextTick events queue and will process all the tasks from the given queue until the queue is empty.

In the example bellow the tasks will be executed in the following order: readFile() -> myNextTickTask() -> myImmediateTask() -> myTimeoutTask()

import { readFile } from 'fs';

readFile('/etc/passwd', (err, data) => {
  if (err) throw err;
  setTimeout(function myTimeoutTask() {
    console.log('This task is from the timers queue');
  })
  setImmediate(function myImmediateTask() {
    console.log('This task is from the immediates queue');
  })
  process.nextTick(function myNextTickTask() {
    console.log('This task is from the nextTick queue');
  })
});

It is important to note that tasks scheduled using process.nextTick() are going to be processed within the current iteration of the event loop, so be carefully when using it.

If you call recursively a function that contains a process.nextTick() you may block the event loop as shown in the following example:


function myFunction() {
    // ...
    // ...
    process.nextTick(() => {
        // ...
        myFunction();
	// ...
    });
    // ...
}

Conclusion

You should now have no doubts about using setTimeout(), setImmediate(), and process.nextTick().

To keep things simple, Node.js recommends to use setImmediate() in all cases as it is predictable and easy to think about.