Understanding setImmediate()
When you want to execute some piece of code asynchronously, but as soon as possible, one option is to use the setImmediate()
function provided by Node.js:
setImmediate(() => {
// run something
});
Any function passed as the setImmediate() argument is a callback that's executed in the next iteration of the event loop.
How is setImmediate()
different from setTimeout(() => {}, 0)
(passing a 0ms timeout), and from process.nextTick()
and Promise.then()
?
A function passed to process.nextTick()
is going to be executed on the current iteration of the event loop, after the current operation ends. This means it will always execute before setTimeout
and setImmediate
.
A setTimeout()
callback with a 0ms delay is very similar to setImmediate()
. The execution order will depend on various factors, but they will be both run in the next iteration of the event loop.
A process.nextTick
callback is added to process.nextTick queue
. A Promise.then()
callback is added to promises microtask queue
. A setTimeout
, setImmediate
callback is added to macrotask queue
.
Event loop executes tasks in process.nextTick queue
first, and then executes promises microtask queue
, and then executes macrotask queue
.
Here is an example to show the order between setImmediate()
, process.nextTick()
and Promise.then()
:
const const baz: () => void
baz = () => var console: Console
console.Console.log(...data: any[]): void
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log('baz');
const const foo: () => void
foo = () => var console: Console
console.Console.log(...data: any[]): void
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log('foo');
const const zoo: () => void
zoo = () => var console: Console
console.Console.log(...data: any[]): void
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log('zoo');
const const start: () => void
start = () => {
var console: Console
console.Console.log(...data: any[]): void
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log('start');
setImmediate(const baz: () => void
baz);
new var Promise: PromiseConstructor
new <any>(executor: (resolve: (value: any) => void, reject: (reason?: any) => void) => void) => Promise<any>
Creates a new Promise.Promise((resolve: (value: any) => void
resolve, reject: (reason?: any) => void
reject) => {
resolve: (value: any) => void
resolve('bar');
}).Promise<any>.then<void, never>(onfulfilled?: ((value: any) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>
Attaches callbacks for the resolution and/or rejection of the Promise.then(resolve: any
resolve => {
var console: Console
console.Console.log(...data: any[]): void
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(resolve: any
resolve);
process.nextTick(const zoo: () => void
zoo);
});
process.nextTick(const foo: () => void
foo);
};
const start: () => void
start();
// start foo bar zoo baz
This code will first call start()
, then call foo()
in process.nextTick queue
. After that, it will handle promises microtask queue
, which prints bar
and adds zoo()
in process.nextTick queue
at the same time. Then it will call zoo()
which has just been added. In the end, the baz()
in macrotask queue
is called.
The principle aforementioned holds true in CommonJS cases, but keep in mind in ES Modules, e.g. mjs
files, the execution order will be different:
// start bar foo zoo baz
This is because the ES Module being loaded is wrapped as an asynchronous operation, and thus the entire script is actually already in the promises microtask queue
. So when the promise is immediately resolved, its callback is appended to the microtask
queue. Node.js will attempt to clear the queue until moving to any other queue, and hence you will see it outputs bar
first.