Skip to content

Instantly share code, notes, and snippets.

@brycebaril
Last active February 23, 2020 22:35
Show Gist options
  • Save brycebaril/ff86eeb90b53fd0c523e to your computer and use it in GitHub Desktop.
Save brycebaril/ff86eeb90b53fd0c523e to your computer and use it in GitHub Desktop.
process.nextTick vs setImmediate

@mafintosh asks: "Does anyone have a good code example of when to use setImmediate instead of nextTick?"

https://twitter.com/mafintosh/status/624590818125352960

The answer is "generally anywhere outside of core".

process.nextTick is barely asynchronous. Flow-wise it is asynchronous, but it will trigger before any other asynchronous events can (timers, io, etc.) and thus can starve the event loop.

In this script I show a starved event loop where I just synchronously block, use nextTick and setImmediate

bryce@x1c:~/tmp$ /usr/bin/time node starved.js blocked
I took 1608.396358ms, expected to take 10
1.68user 0.00system 0:01.69elapsed 99%CPU (0avgtext+0avgdata 24016maxresident)k
0inputs+0outputs (0major+3572minor)pagefaults 0swaps
bryce@x1c:~/tmp$ /usr/bin/time node starved.js nexttick
I took 1611.755809ms, expected to take 10
1.68user 0.00system 0:01.70elapsed 99%CPU (0avgtext+0avgdata 24024maxresident)k
0inputs+0outputs (0major+3572minor)pagefaults 0swaps
bryce@x1c:~/tmp$ /usr/bin/time node starved.js setimmediate
I took 144.870217ms, expected to take 10
1.68user 0.00system 0:01.69elapsed 99%CPU (0avgtext+0avgdata 23952maxresident)k
0inputs+0outputs (0major+3578minor)pagefaults 0swaps

Notice the blocked and nexttick behave the same -- the setTimeout is delayed until after all calls to the blocking run function.

Even though we're scheduling more process.nextTick calls asynchronously, nothing can get inserted between them on the event loop.

The setImmediate example is not blocked as long because the setTimeout gets run between consecutive setImmediate calls, and even though it is late (the run function is slow enough to delay it on its own) it still gets through.

var loops = 11
function run() {
loops--
for (var i = 0; i < 1e7; i++) {
Math.pow(Math.random(), Math.random())
}
if (loops > 0) {
if (process.argv[2] == "blocked") {
run()
}
if (process.argv[2] == "nexttick") {
process.nextTick(run)
}
if (process.argv[2] == "setimmediate") {
setImmediate(run)
}
}
}
var delay = 10
var start = process.hrtime()
setTimeout(function () {
var elapsed = process.hrtime(start)
var ms_elapsed = (elapsed[0] * 1000) + (elapsed[1] / 1e6)
console.log("I took %sms, expected to take %s", ms_elapsed, delay)
}, delay)
run()
@trevnorris
Copy link

Notice the blocked and nexttick behave the same

Mostly the same. The blocking call builds the call stack until the application crashes. The nextTick can run indefinitely.

@ackuser
Copy link

ackuser commented Jan 3, 2016

var emitter = new require('events').EventEmitter();

setTimeout(function() {
console.log('TIMEOUT 1');
}, 0);

setImmediate(function() {
    console.log("IMMEDIATE 1");
});

process.nextTick(function() {
    console.log("NEXTTICK 1");
});


setTimeout(function() {
  setTimeout(function() {
    console.log('TIMEOUT 2')
  }, 0);
  setImmediate(function() {
    console.log('IMMEDIATE 2')
  });
}, 10);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment