Peter Lyons

setTimeout and Friends

March 12, 2014

So I was recently scheduled to do an AirPair pair programming session to help someone understand the various javascript functions available for asynchronous programming.

This topic is key to writing correct code both in node.js and the browser, but it is indeed easy to see how things can be quite confusing when first encountering a myriad of functions that all seem to basically do the same thing.

setTimeout, setImmediate, process.nextTick? Which one is best? What are the differences?

The basic concept at work here is normally in javascript your code executes "now" meaning line by line, one thing after another, until there's no more code. However, with a user interacting with the browser or with node.js code responding to HTTP requests, we need a way to ask the runtime to run some code "later". And of course using event handlers we already have a mechanism to ask the runtime to execute our code "whenever X happens" where X is a mouse click, keystroke, etc in the browser or a database call returning in node.js.

So I went and researched this and put together what I hope to be a mostly comprehensive overview of all of the relevant functions and some of the tricky points to be aware of. The good news is mostly this stuff is actually pretty straightforward once you get the basic handle of it, and there are just a few key subtle gotchas that take a little more practice to wrangle.

setTimeout(functionToCall, msToWait)

clearTimeout(timeoutID)

setInterval(functionToCall, intervalMs)

clearInterval(intervalID)

process.nextTick(functionToCall)

setImmediate

clearImmediate

"naming stuff is hard" --@izs

setImmediate happens on the NEXT tick.

process.nextTick happens on the SAME tick (usually, with caveat about maxTickDepth).

Which one to use in node.js

Generally setImmediate is the best practice starting with node.js v0.10, so that's what you should use. However, if you need to support node v0.8, using process.nextTick is still fine. The whole problem of I/O starvation doesn't occur in most projects. It's only in certain types of code that are either misguided and poorly coded or truly have a weird edge case. For your run-of-the-mill node.js callback, either is just fine but setImmediate is better.

Reference Articles

Isaac Schlueter's comment on The Case for setImmediate:

I agree the point you’re making here, 100%. However, a slight correction about Node’s APIs.

First of all, process.nextTick is actually first in, first out. Proof:

$ node -e 'process.nextTick(console.log.bind(console, 1)); process.nextTick(console.log.bind(console, 2))' 1 2

Second, process.nextTick isn’t quite the same as setImmediate, at least as of 0.10. Node has a setImmediate function which does happen on the next turn of the event loop, and no sooner. process.nextTick happens before the next turn of the event loop, so it is not suitable for deferring for I/O. It IS suitable for deferring a callback until after the current stack unwinds, but making sure to call the function before any additional I/O happens. In other words, every entrance to JS from the event loop goes like:

Event loop wakes up (epoll, etc.)Do the thing that you said to do when that event happens (call handle.onread or whatever)Process the nextTick queue completelyReturn to the loop

setImmediate is something like a timer that schedules an immediate wake-up, but on the next turn of the event loop. (It’s been pointed out that they’re named incorrectly, since setImmediate is on the next “tick”, and process.nextTick is “immediate”, but whatever, naming stuff is hard, and we’re borrowing the bad name from the browser spec.)

The difference may seem like a minor nit-pick, but it’s actually quite relevant. If you are recursively calling process.nextTick, then you’re never actually yielding to the event loop, so you’re not accepting new connections, reading files and sockets, sending outgoing data, etc.

But that aside, yes. It is completely baffling to me that there is any resistance to this API in browsers, where it is so clearly an obvious win.

Thanks to @derickbailey and @sambreed for reviewing and editing a draft this post.