setImmediate() vs nextTick() vs setTimeout(fn,0) – in depth explanation

Few days back, I was guiding some new node.js developers on making asynchronous stuffs. We were discussing about async apis of node js. I wanted to provide them with some references and googled for few; but surprisingly, majority of the articles out there in the internet about setImmediate() or process.nextTick() was containing insufficient or misleading information. And going through official documents of Node may not really be feasible for non-advanced developers. Hence I decided to come up with this article.

Know the misconceptions first

Before I start describing anything, I would like to clear some of the misconceptions of the other articles, covering this topic. If you are not misled yet, you can skip this section.

setImmediate() runs before setTimeout(fn, 0)

This is one of the most common misconceptions. I will discuss about the right concepts later on this article, but below is a proof of this statement being false.

If the statement above was true; running the above code would have given an output where SETIMMEDIATE would have been printed always before SETTIMEOUT. However in reality, the output of the above is not predictable. If you run node index.js multiple times, you will find multiple orders.

setImmediate() puts the callback ahead of the job queue

If the statement above was true; it would have produced the following output.

But the actual output is like the following; irrespective of how many times you run it.

nextTick() triggers the callback on next tick (iteration)

Actually both process.nextTick() and setImmediate() was named wrongly. If we swap the names of those then the names will match the functionality. However as in JavaScript, they do not deprecate/change apis, so the named continued as wrong.
In terms of functionality, process.nextTick() is actually the way to invoke a callback immediately. Callback in setImmediate() will be triggered during/next iteration.

How node.js event loop works

The only way to understand the workflow and the differences between these three functions; you must understand the functioning of the event loop. Hope you already know that event loop handles all async callbacks, but here we will discuss how it does so.

Though I am providing a short description of event loop here; but if you want to know it properly, you should read in depth explanation of event loop structure and workflow.


Each rectangular box in the diagram represent a phase and event loops iterates on those again and again, starting from timers to close callbacks. There is also a nextTickQueue in the middle, however it’s not a part of the event loop itself. Each phase has a queue attached to it. When event loop enters in a particular phase, its target is to execute the callbacks/tasks in those queues. A little description about the phases are as below.

Timer: It handles the callbacks assigned by setTimeout & setInterval after the given time threshold is completed.
I/O callbacks: Handles all callbacks except the ones set by setTimeout, setInterval & setImmediate. It also does not have any close callbacks.
Idle, prepare: Used internally.
Pole: Retrieve new I/O events. This is which makes node a cool dude.
Check: Here the callbacks of setImmediate() is handled.
Close callbacks: Handles close connection callbacks etc. (eg: socket connection close)
nextTickQueue: Holds the callbacks of process.nextTick(); but not a part of the event loop.

How event loop propagates

It enters the Timer phase & checks if anything (callback) is there in the timer queue. If there are some, it starts executing one after another till either the queue is empty or the maximum allowed callback execution is completed.

After Timer it moves to the I/O callback phase where it again find the queue associated with it for i/o operations. It followed the similar approach as timer and after task done moves to the next phase.

Idle phase is used by node internally; for preparation etc. After that, the event loop enters the Poll phase where it handles events. If there is no event to be handled then also the event loops waits a bit in the poll phase for new i/o events. Nothing in the event loops works when poll phase is in waiting or sleep mode. However if there are some scripts assigned by setImmediate the event loop will end the poll phase and continue to the Check phase to execute those scheduled scripts.

After Check it will try executing anything in Close callbacks and after that goes back to Timer for the next iteration or tick.

Now about nextTickQueue. Any callbacks assigned by process.nextTick() is queued in the nextTickQueue and the event loop executes them one after another another, till the entire queue is drained out; after completing the ongoing operation; irrespective of which phase it is in.

This concludes the event loop description and now we may try to understand the three apis mentioned in the title of this article.


So first of all, by the workflow of event loop, now we can say setImmediate() is not exactly immediate, but the queue containing the callbacks of this, will be executed once in every iteration (when event loop is in Check phase).

So, the example in previous section; things were non-deterministic, because it depends on the performance of the process. Cause timer has an extra work of sorting, which takes some extra time to register it. However if we move the piece of code in an I/O callback; we can guarantee that the callback of setImmediate will be called before setTimeout, irrespective of anything else.


This also invokes the callback, but will not be executed till the event loop enters the Timer phase. So any setTimeout(fn, 0) along with setImmediate() in the Close callback phase will guarantee the execution of setTimeout 0 before the setImmediate. And accordingly, keeping the phase diagram of event loop in your mind, you can easily determine whether it’s setTimeout(fn, 0) or setImmediate() which will be called at the earliest.


As per node.js documentation, “nextTickQueue will be processed after the current operation completes, regardless of the current phase of the event loop.”
It means, this queue will be executed whenever the boundary between JavaScript and C/C++ is crossed. So it’s not like it will be called after the task in the current phase only. Neither it means after the execution of the current callback. It is sometime before the next phase is hit.

About This Author

Hello! I am Paul Shan, a JavaScript Expert, Full Stack and DevOps Engineer cum Consultant based out of Bengaluru, India.

  • WWII

    This is by far the best illustration of the topic..

    • Ano Nym

      Yeah, I agree!

    • Paul Shan

      Thanks 🙂

  • Павел Ашифин

    Very good explanation!
    Thanks a lot!

  • Christian Andrade

    agree, is the best illustration of the topic

  • One of the best explanations I’ve ever read about the event loop!

  • Jason Lee

    Thanks for clarifying this up! But I still have a question with the code below, you can see that using setImmediate() is significantly faster than setTimeout(, 0), how do you explain this fact?

    var i = 0;
    var start = new Date();
    function foo () {

    if (i < 1000) {
    // this is much slower
    // setTimeout(foo, 0);
    } else {
    var end = new Date();
    console.log(end – start);


  • Ravi I

    In the answer to Jason Lee’s question you wrote this ‘Few callbacks were pulled from check phase, executed them, then after the maximum allowed execution in check phase it tries to complete another tick and reaches the timer phase’. But when foo() executes for the first time only one foo() is added to check phase queue. Then it checks for timer queue but there is nothing because of 1ms delay and then it executes the foo() in check queue which adds only one foo() to the check queue again. This time there might be a one or few logs in timer queue which get executed and then the foo() in check queue executes again which pushes another foo() into check phase and it goes on. so here is my question. What do you mean by the statement I mentioned above like why are there multiple foo()’s in the check queue according to your explanation. Can you please explain more on that and also would you write more about when the event loop gets initiated and from when and which phase does the execution start in different scenarios. Sorry for the long post but the event loop explanation got me confused and didn’t find anything on the web for my question.

    • Paul Shan

      At any given time, there is only 1 foo in the check queue.
      But, unlike setTimeout (minimum 1 ms delay), setImmediate doesn’t have any waiting time.
      Now, suppose, the first foo is getting executed after pulling it from the check queue. While executing it will invoke another foo in the check queue, cause there is no 1ms delay. So right after finishing the execution of the first foo, it will find that there is another foo in the check queue and will execute it.
      Whereas in case of setTimeout, it won’t find the second foo right after finishing the the first, because of the 1ms delay.

    • Ravi I

      Thanks for the clarification. That helped a lot.

  • ScreamZ

    Awesome explanation… Thank you very much.

    But I still have a question : Do I have any control on the “maximum allowed callback execution.’ per phase ? Or is it automatically handled by the engine ?

    • Paul Shan

      You can’t modify that programatically. But both node & v8 are open-source. You can can modify the source-code and make a modified-v8

  • Guy Crazy

    Thanks a lot !
    But I have a question : If one event emit , where the event handler(callback) will be execute , is in poll phase or in I/O callbacks phase ;
    And what is the difference between I/O callbacks phase and poll phase, that confuse me ….

    • Paul Shan

      Callback execution is part of core JavaScript. V8 or other javascript engines handle that.
      Event loop is not part of the javascript engine. It is developed separately by the consumer (by consumer I mean node, google-chrome etc).
      I/O is for internal input outputs… like file reading etc. Poll is for user http requests.

  • Guy Crazy

    i meet a case, that conflict with one point in your essay
    ( ‘So if the event loop is in Timer and there were 5 callbacks in the timer queue already; and event loop is busy executing the third one. By that time if few process.nextTick() callbacks are pushed to nextTickQueue, the event loop will execute all of them synchronously after completing the current callback execution (which is 3rd one) and will resume the Timer callback execution again from the 4th callback. ‘)

    code :

    setTimeout(() => {
    setTimeout(() => {
    }, 0);

    var waitTill = new Date(new Date().getTime() + 5 * 1000);
    while (waitTill > new Date()) { }

    console.log(‘excute at this line now’);
    process.nextTick(() => {
    }, 2);

    setTimeout(() => {
    }, 2);

    setTimeout(() => {
    }, 4);

    result :
    excute at this line now

    why ccc is print after bbb, if follow your article’s point of view , ccc should print before bbb ,
    and another question is why ddd print last ?

    Did i miss something ?


    • Paul Shan

      Regarding Q1:
      The exact word which node api doc used is, “operation” not callback. Their exact sentence is “Instead, the nextTickQueue will be processed after the current operation completes.”
      As computer science doesn’t have any fixed definition of “operation”, I understood it as the current execution context; i.e. current callback.
      Your example shows the operation doesn’t mean a callback. Thanks for correcting me. I’ve modified that part in the article. Seems like nextTick queue is not as immediate to run right after current callback; but definitely before leaving that phase. I will try to contact node people to get more insights on the word “operation”.

      Regarding Q2:
      As this article is not about eventLoop, but just shows the differences between few apis of node, I didn’t explained very low level parts here and have presented an abstracted view. I’m working on an in depth explaination of event loop; expected to get published by first week of feb.
      Timer callbacks are executed in timer phase, but when should they be pushed into timer queue, is decided in the poll phase.
      When you use setTimeout, it basically adds the information in a memory help. In poll, node checks if anything in the timer-heap is exhausted and if it finds one, it pushed the callback of that in the timer queue. And then whenever event loop enter timer phase next, it executes that callback.
      In your example when you invoked ddd setTimeout, it added it in the timer heap memory and the event loop has to go to poll phase to pull the callback to the timer queue.

    • Guy Crazy

      Good explanation. Very helpful !

    • msulc

      The order of ‘bbb’/’ccc’ in GuyCrazy’s example is not really guaranteed. The reason for this is how Node handles setTimeout callbacks in lib/timers.js. In short, these callbacks are stored in linked lists grouped by the corresponding delay time. Now, if one callback fires, Node determines its group, marks current time t_now and processes all callbacks within the group. For each one, it knows its registration time t_reg and delay time delta. If (t_now – t_reg >= delta), it invokes the callback. The thing is that t_now is evaluated only once for the entire group.

      To illustrate this, I changed in the mentioned example the delay times of 2 and 4 to 200 and 400. If I compile Node in debug mode (and modify function listOnTimeout in lib/timers.js in order to show the _idleStart parameter) and execute the example (with environmental variable NODE_DEBUG=timer), I get:

      TIMER 32895: no 200 list was found in insert, creating a new one
      TIMER 32895: no 400 list was found in insert, creating a new one
      TIMER 32895: timeout callback 200
      TIMER 32895: now: 262
      TIMER 32895: _idleStart = 60
      TIMER 32895: no 1 list was found in insert, creating a new one excute at this line now
      TIMER 32895: _idleStart = 62
      TIMER 32895: 200 list empty
      TIMER 32895: timeout callback 1
      TIMER 32895: now: 5263
      TIMER 32895: _idleStart = 263
      TIMER 32895: 1 list empty
      TIMER 32895: timeout callback 400
      TIMER 32895: now: 5264
      TIMER 32895: _idleStart = 62
      TIMER 32895: 400 list empty

      Here, we see that the ‘aaa’ and ‘bbb’ callbacks were scheduled at time t_a=60 and t_b=62, respectively. When callback ‘aaa’ fires at time T = 262, it takes more or less 5s to execute it. Once this execution finishes, Node continues within the same group of callbacks (i.e., callbacks associated with the same delay time) thus checking the ‘bbb’ callback. However, it takes into account as current time not the time after the execution of the ‘aaa’ callback but the time T when it entered the listOnTimeout function. On my machine, since the ‘bbb’ callback was registered at time 62, 262 – 62 is exactly 200 and thus ‘bbb’ “luckily” fires as well. Node then proceeds with the nextTick operation and the remaining callbacks.

      To be more specific, since the ‘aaa’ callback fires, we know that T – t_a >= delta. However, the relationship between T – t_b and delta is not guaranteed. To illustrate this, one can add the waiting loop with, e.g., 50ms before the call of setInterval which schedules the ‘bbb’ callback. The output I get is then:

      TIMER 33177: no 200 list was found in insert, creating a new one
      TIMER 33177: no 400 list was found in insert, creating a new one
      TIMER 33177: timeout callback 200
      TIMER 33177: now: 269
      TIMER 33177: _idleStart = 63
      TIMER 33177: no 1 list was found in insert, creating a new one
      excute at this line now
      TIMER 33177: _idleStart = 114
      TIMER 33177: 200 list wait because diff is 155
      TIMER 33177: timeout callback 1
      TIMER 33177: now: 5270
      TIMER 33177: _idleStart = 270
      TIMER 33177: 1 list empty
      TIMER 33177: timeout callback 400
      TIMER 33177: now: 5270
      TIMER 33177: _idleStart = 114
      TIMER 33177: 400 list empty
      TIMER 33177: timeout callback 200
      TIMER 33177: now: 5272
      TIMER 33177: _idleStart = 114
      TIMER 33177: 200 list empty

      We see here that in this case the ‘bbb’ callback was added to the list a bit later at time 114. Now when ‘aaa’ fires at time 269, Node process the entire group of callbacks associated with delay time of 200 as mentioned above, but in this case 269 – 114 < 200 so that the execution of 'bbb' is postponed (the corresponding timer is restarted with delay of 115 with respect to the current real time, therefore it will in fact fire even after the callback scheduled originally with delay of 400) and thus Node proceeds directly to the nextTick operation.

    • msulc

      The order of the ‘bbb’/’ccc’ calls in GuyCrazy’s example is not really guaranteed. The reason for this is how Node handles setTimeout callbacks in lib/timers.js. In short, these callbacks are stored in linked lists grouped by the corresponding delay time (so callbacks with identical delay times are “treated collectively”). Now, if one callback fires, Node determines its group, marks current time t_now and processes all callbacks within the group. For each one, it knows its registration time t_reg and delay time delta. If (t_now – t_reg >= delta), it invokes the callback. However, t_now is evaluated only once for the entire group so that even though the condition on delta is satisfied for the first callback of the group, there is no guarantee that it will hold also for the remaining ones.

    • Paul Shan

      Thanks for sharing the grouping concept. Didn’t know about that. Ran some experiments and it seems true.
      However, timer executes callbacks in groups, but timer queue only holds elapsed timer callbacks. And whether a timer is elapsed or not, is something decided by the poll. So the second part you mentioned is actually done in poll.

    • Pranay Pant

      I have 3 questions:

      1. What is poll phase exactly? Like, what does “handle event I/O” mean? It handles callbacks from events library(EventEmitter)?
      2. Is the while loop executed synchronously or given to a phase in the event loop? If so, what phase?
      3. What phase handles ‘regular’ JS commands like console.log, variable assignment etc. What phase ‘starts’ the whole loop when we run ‘node index.js’ etc.

    • Paul Shan

      1. Poll is kind of master phase. It receives events, data, connections etc. It takes decisions and pushes callback to timer and i/o queue. If you don’t know, EventEmitter is synchronous and thus have no role in poll.
      2. Synchronously.
      3. Every phase executes JS. When I say execute callback, that means executing the JS code in the callback. Every phase can interact with v8 (synchronously). When you run node inde.js; it initializes the event loop but do not enter. Node executes your main module first (outside event loop) and then enter the loop by the timer phase.

      As the article is not about eventLoop mainly; I didn’t put much in depth details. But as most of the visitors here are stuck with eventLoop understanding, I’m gonna post separate post on in depth workflow of event loop (till libUV). Stay tuned.

  • Tor M. Hammeren

    Hi! Very interesting stuff. Just one thing about the page. It is difficult to read because of the very light gray color. At least on my mobile.

    • Paul Shan

      The background of the texts are white only. Which mobile and browser are you using? If you can send a screenshot to it will be helpful.
      About the article, it is also kind of a shorthand of the actual event loop. I’m gonna publish a brief and the a to z workflow of eventloop in few weeks. Stay tuned.

    • Tor M. Hammeren

      Chrome on Samsung S8. Yes the background is white, but the text is almosr as white as the background. This makes the text almost invisible.

  • Ramakrishnan Iyer

    I’ve written the following piece of code:
    var fs = require(‘fs’);


    console.log(“Set Immediate!!!”);

    console.log(‘set timeout!!!’);

    Here the expected outcome was :
    set timeout!!!
    Set Immediate!!!

    But I’m getting:
    Set Immediate!!!
    set timeout!!!

    Can you please explain ?

    • Paul Shan

      After the i/o cycle node enters the check phase. Thus setImmediate will be printed. You got the right outcome

    • Omprakash Sharma

      you are so awesome man. Please guide me how to start with node.js

    • Paul Shan

      First try to learn vanilla JavaScript (no frameworks).If you are good at JS, you are good at node.
      After that try building small api projects using it. Use express.js etc… or may be loopback for quicker development.
      Practice will make you an intermediate from beginner. And continuous digging and reading about node will eventually make you a master.

      We also write a lot on Node. You can subscribe if you want.

  • Priya Das

    Can you please explain how the thread pool works in node js

  • Deepak Pathak

    setImmediate(), in comparison to setTimeout(), will always execute first, if inside an I/O cycle.
    If on main module, then, it would be non-deterministic and would be based on performance of the process/system.

  • aman

    Hi Paul, Nice explantion.

    But here I am stuck in:

    I run the following code

    setTimeout(function () {
    }, 0);
    setImmediate(function () {
    console.log(‘setImmediate (-_-) (-_-) (-_-) (-_-) (-_-) (-_-) (-_-) (-_-)’);
    console.log(‘console end’);

    Every time it gives me this output

    console end

    But when I put the console.log at top like this

    console.log(‘console start’);
    setTimeout(function () {
    }, 0);
    setImmediate(function () {

    The squence of setTimout Call back and SetImmediate callback vary every time.

    Can you Please explain this ?

    • Paul Shan

      when you put console.log() or any other operation after setting up the setTimeout, it buys the timeout more time to settle down. Thus, when the control enters the timer phase, it has more probability to find the expired timeout.

    • aman

      Thank you for you reply first,

      What about this snippet of code..

      process.nextTick(function () {
      console.log(‘This is process next Tick’);
      setTimeout(function () {
      console.log(‘This is timeout’);
      }, 0);
      setImmediate(function () {
      console.log(‘This is immediate’);

      Every time , it gives me this output

      This is process next Tick
      This is timeout
      This is immediate

      Why not timeout an immediate squence changes here.

    • Paul Shan

      Similar to the previous. Buys more time after invoking the setTimeout as there is a callback to be executed before the control enters the timer phase.