setImmediate vs. nextTick
Asked Answered
C

8

387

Node.js version 0.10 was released today and introduced setImmediate. The API changes documentation suggests using it when doing recursive nextTick calls.

From what MDN says it seems very similar to process.nextTick.

When should I use nextTick and when should I use setImmediate?

Cilurzo answered 11/3, 2013 at 22:14 Comment(6)
There are 5 paragraphs about this change on the blog blog.nodejs.org/2013/03/11/node-v0-10-0-stablePlatina
From performance benchmarks it looks like nextTick is faster than setImmediate on large calculations.Iorgos
For the record, I read those five paragraphs first and still ended up on this question when it didn't really clear anything up for me. The accepted answer is much more concise and actually describes what setImmediate does in better detail.Undertrump
I've explained the difference in great detail in my blog.Coloquintida
Is it the case that GC can run before setImmediate, but not before nextTick?Use
That is a separate question but no - GC can run whenever GC wants to run. It is true that setImmediate gives GC a more appropriate opportunity to run over a clearer scope (an external event) but Node doesn't manually pause GC for other code. You can always run GC yourself with --expose-gc by the way or trace GCs and see :)Cilurzo
Z
564

Use setImmediate if you want to queue the function behind whatever I/O event callbacks that are already in the event queue. Use process.nextTick to effectively queue the function at the head of the event queue so that it executes immediately after the current function completes.

So in a case where you're trying to break up a long running, CPU-bound job using recursion, you would now want to use setImmediate rather than process.nextTick to queue the next iteration as otherwise any I/O event callbacks wouldn't get the chance to run between iterations.

Zenobia answered 11/3, 2013 at 22:26 Comment(16)
Why would I not use process.nextTick all the time then? Hope you will answer, all this nextTick is confusing. Thanks.Sigil
Callbacks passed to process.nextTick will usually be called at the end of the current flow of execution, and are thus approximately as fast as calling a function synchronously. Left unchecked, this would starve the event loop, preventing any I/O from occurring. setImmediates are queued in the order created, and are popped off the queue once per loop iteration. This is different from process.nextTick which will execute process.maxTickDepth queued callbacks per iteration. setImmediate will yield to the event loop after firing a queued callback to make sure I/O is not being starved.Cilurzo
In the browser world, setTimeout(func, 0) works as process.nextTick() but there is no equivalent for setImmediate(). I was however told that the behavior of process.nextTick() has changed, has it?Shanna
@UstamanSangat setImmediate is a part of the DOM API and some browsers already support it. nextTick will now throw a warning if it thinks it is inappropriately used (try to split in recursion in it and see!)Cilurzo
@UstamanSangat setImmediate is supported by IE10+ only, all other browsers are stubbornly refusing to implement the likely future standard because they don't like being beaten by Microsoft. To achieve a similar result in FF/Chrome, you can use postMessage (post a message to your own window). You could consider using requestAnimationFrame as well, especially if your updates are UI-related. setTimeout(func, 0) does not work like process.nextTick at all.Milewski
@Milewski "because they don't like being beaten my Microsoft" makes you sound sore about something. It's mostly because it's terribly, terribly named. If there's one time setImmediate function will never, ever run, it's immediately. The name of the function is the exact opposite of what it does. nextTick and setImmediate would be better off switching around; setImmediate executes immediately after the current stack completes (before waiting I/O) and nextTick executes at he end of the next tick (after waiting I/O). But then, this has been said a thousand times already.Ubiquitarian
@CraigAndrews You said it yourself. "setImmediate executes immediately after the current stack completes". It sets a function to be called back immediately, when control is returned from the currently running script. I dare you to think of a simpler and equally memorable name for it.Milewski
@Milewski But unfortunately the function is called nextTick. nextTick executes "immediately" while setImmediate is more like a setTimeout/postMessage.Lissie
@Lissie Well, if nextTick was available in web browsers it would make sense to have a pair of functions named nextTick and laterTick, or as you suggest, setImmediate and setLater or something. But nextTick is not relevant to web browsers today, because it is not available. What names would you propose?Milewski
@CraigAndrews I would avoid requestAnimationFrame because it doesn't always occur (I have definitely seen this, I think example was tab wasn't current tab) and it can get called before the page has completed painting (i.e. browser is still busy drawing).Adenaadenauer
JohnnyHK said: "Use setImmediate if you want to queue the function behind whatever I/O event callbacks that are already in the event queue." Craig Andrews said: "setImmediate executes immediately after the current stack completes (before waiting I/O)" I thought setImmediate is supposed to be used in recursive calls "to avoid pre-empting the I/O event loop for long-running jobs." (i.e. AFTER waiting I/O) Was that a typo from Craig?Graybeard
@Graybeard I think Craig Andrews was talking about how the functions would make a lot more sense if the names were swapped.Peonir
just shim it if you really need it. window.setImmediate = window.setImmediate || function(fn){ setTimeout(fn, 10); };Isotonic
requestAnimationFrame is a hack and IE does not seem to be able to handle a lot of requestAnimationFrame operations at once (Chrome and Firefox don't appear to suffer from the same issues)Oliver
It should be noted that setImmediate exits the active domain, process.nextTick does not. I'm fully aware that node domains are "deprecated" (and almost comically so).Metallurgy
@Isotonic That is not a proper shim for setImmediate. They might as well just use setTimeout(fn, 10) directly. Misses the point of setImmediate completely.Orthoepy
A
108

As an illustration:

import fs from 'fs';
import http from 'http';
    
const options = {
  host: 'www.stackoverflow.com',
  port: 80,
  path: '/index.html'
};

describe('deferredExecution', () => {
  it('deferredExecution', (done) => {
    console.log('Start');
    setTimeout(() => console.log('setTimeout 1'), 0);
    setImmediate(() => console.log('setImmediate 1'));
    process.nextTick(() => console.log('nextTick 1'));
    setImmediate(() => console.log('setImmediate 2'));
    process.nextTick(() => console.log('nextTick 2'));
    http.get(options, () => console.log('network IO'));
    fs.readdir(process.cwd(), () => console.log('file system IO 1'));
    setImmediate(() => console.log('setImmediate 3'));
    process.nextTick(() => console.log('nextTick 3'));
    setImmediate(() => console.log('setImmediate 4'));
    fs.readdir(process.cwd(), () => console.log('file system IO 2'));
    console.log('End');
    setTimeout(done, 1500);
  });
});

will give the following output

Start // synchronous
End // synchronous
nextTick 1 // microtask
nextTick 2 // microtask
nextTick 3 // microtask
setTimeout 1 // macrotask
file system IO 1 // macrotask
file system IO 2 // macrotask
setImmediate 1 // macrotask
setImmediate 2 // macrotask
setImmediate 3 // macrotask
setImmediate 4 // macrotask
network IO // macrotask

I hope this can help to understand the difference.

Updated:

Callbacks deferred with process.nextTick() run before any other I/O event is fired, while with setImmediate(), the execution is queued behind any I/O event that is already in the queue.

Node.js Design Patterns, by Mario Casciaro (probably the best book about node.js/js)

Arte answered 3/2, 2017 at 18:38 Comment(3)
I think it's important to point out that setTimeout() and setImmediate() when not within an I/O cycle the order is non-deterministic depending on the performance of a process. nodejs.org/en/docs/guides/event-loop-timers-and-nexttick For example, if we run the following script which is not within an I/O cycle (i.e. the main module), the order in which the two timers are executed is non-deterministic, as it is bound by the performance of the process: So, this answer doesn't really answer the exact difference but merely an example that could vary in different contextLater
As pointed out by @Actung. it is very important to know whether setTimetout and setImmediate are within an I/O cycle or not, to determine the outcome.Loggia
Why is I01 at the very end?Legion
U
76

I think I can illustrate this quite nicely. Since nextTick is called at the end of the current operation, calling it recursively can end up blocking the event loop from continuing. setImmediate solves this by firing in the check phase of the event loop, allowing event loop to continue normally.

   ┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<─────┤  connections, │
│  └──────────┬────────────┘      │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
   └───────────────────────┘

source: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/

Notice that the check phase is immediately after the poll phase. This is because the poll phase and I/O callbacks are the most likely places your calls to setImmediate are going to run. So ideally most of those calls will actually be pretty immediate, just not as immediate as nextTick which is checked after every operation and technically exists outside of the event loop.

Let's take a look at a little example of the difference between setImmediate and process.nextTick:

function step(iteration) {
  if (iteration === 10) return;
  setImmediate(() => {
    console.log(`setImmediate iteration: ${iteration}`);
    step(iteration + 1); // Recursive call from setImmediate handler.
  });
  process.nextTick(() => {
    console.log(`nextTick iteration: ${iteration}`);
  });
}
step(0);

Let's say we just ran this program and are stepping through the first iteration of the event loop. It will call into the step function with iteration zero. It will then register two handlers, one for setImmediate and one for process.nextTick. We then recursively call this function from the setImmediate handler which will run in the next check phase. The nextTick handler will run at the end of the current operation interrupting the event loop, so even though it was registered second it will actually run first.

The order ends up being: nextTick fires as current operation ends, next event loop begins, normal event loop phases execute, setImmediate fires and recursively calls our step function to start the process all over again. Current operation ends, nextTick fires, etc.

The output of the above code would be:

nextTick iteration: 0
setImmediate iteration: 0
nextTick iteration: 1
setImmediate iteration: 1
nextTick iteration: 2
setImmediate iteration: 2
nextTick iteration: 3
setImmediate iteration: 3
nextTick iteration: 4
setImmediate iteration: 4
nextTick iteration: 5
setImmediate iteration: 5
nextTick iteration: 6
setImmediate iteration: 6
nextTick iteration: 7
setImmediate iteration: 7
nextTick iteration: 8
setImmediate iteration: 8
nextTick iteration: 9
setImmediate iteration: 9

Now let's move our recursive call to step into our nextTick handler instead of the setImmediate.

function step(iteration) {
  if (iteration === 10) return;
  setImmediate(() => {
    console.log(`setImmediate iteration: ${iteration}`);
  });
  process.nextTick(() => {
    console.log(`nextTick iteration: ${iteration}`);
    step(iteration + 1); // Recursive call from nextTick handler.
  });
}
step(0);

Now that we have moved the recursive call to step into the nextTick handler things will behave in a different order. Our first iteration of the event loop runs and calls step registering a setImmedaite handler as well as a nextTick handler. After the current operation ends our nextTick handler fires which recursively calls step and registers another setImmediate handler as well as another nextTick handler. Since a nextTick handler fires after the current operation, registering a nextTick handler within a nextTick handler will cause the second handler to run immediately after the current handler operation finishes. The nextTick handlers will keep firing, preventing the current event loop from ever continuing. We will get through all our nextTick handlers before we see a single setImmediate handler fire.

The output of the above code ends up being:

nextTick iteration: 0
nextTick iteration: 1
nextTick iteration: 2
nextTick iteration: 3
nextTick iteration: 4
nextTick iteration: 5
nextTick iteration: 6
nextTick iteration: 7
nextTick iteration: 8
nextTick iteration: 9
setImmediate iteration: 0
setImmediate iteration: 1
setImmediate iteration: 2
setImmediate iteration: 3
setImmediate iteration: 4
setImmediate iteration: 5
setImmediate iteration: 6
setImmediate iteration: 7
setImmediate iteration: 8
setImmediate iteration: 9

Note that had we not interrupted the recursive call and aborted it after 10 iterations then the nextTick calls would keep recursing and never letting the event loop continue to the next phase. This is how nextTick can become blocking when used recursively whereas setImmediate will fire in the next event loop and setting another setImmediate handler from within one won't interrupt the current event loop at all, allowing it to continue executing phases of the event loop as normal.

PS - I agree with other commenters that the names of the two functions could easily be swapped since nextTick sounds like it's going to fire in the next event loop rather than the end of the current one, and the end of the current loop is more "immediate" than the beginning of the next loop. Oh well, that's what we get as an API matures and people come to depend on existing interfaces.

Undertrump answered 31/5, 2017 at 3:19 Comment(1)
it is important to reiterate Node's warning about the use of process.nextTick. If you enqueue a large number of callbacks in the nextTickQueue, you can potentially starve the event loop by ensuring the poll phase is never reached. This is reason why you should generally prefer setImmediate.Febrifacient
P
32

In the comments in the answer, it does not explicitly state that nextTick shifted from Macrosemantics to Microsemantics.

before node 0.9 (when setImmediate was introduced), nextTick operated at the start of the next callstack.

since node 0.9, nextTick operates at the end of the existing callstack, whereas setImmediate is at the start of the next callstack

check out https://github.com/YuzuJS/setImmediate for tools and details

Parmer answered 27/1, 2015 at 1:44 Comment(0)
H
16

Some great answers here detailing how they both work.

Just adding one that answers the specific question asked:

When should I use nextTick and when should I use setImmediate?


Always use setImmediate.


The Node.js Event Loop, Timers, and process.nextTick() doc includes the following:

We recommend developers use setImmediate() in all cases because it's easier to reason about (and it leads to code that's compatible with a wider variety of environments, like browser JS.)


Earlier in the doc it warns that process.nextTick can lead to...

some bad situations because it allows you to "starve" your I/O by making recursive process.nextTick() calls, which prevents the event loop from reaching the poll phase.

As it turns out, process.nextTick can even starve Promises:

Promise.resolve().then(() => { console.log('this happens LAST'); });

process.nextTick(() => {
  console.log('all of these...');
  process.nextTick(() => {
    console.log('...happen before...');
    process.nextTick(() => {
      console.log('...the Promise ever...');
      process.nextTick(() => {
        console.log('...has a chance to resolve');
      })
    })
  })
})

On the other hand, setImmediate is "easier to reason about" and avoids these types of issues:

Promise.resolve().then(() => { console.log('this happens FIRST'); });

setImmediate(() => {
  console.log('this happens LAST');
})

So unless there is a specific need for the unique behavior of process.nextTick, the recommended approach is to "use setImmediate() in all cases".

Hallelujah answered 15/3, 2019 at 4:25 Comment(0)
A
11

In simple terms, process.NextTick() would executed at next tick of event loop. However, the setImmediate, basically has a separate phase which ensures that the callback registered under setImmediate() will be called only after the IO callback and polling phase.

Please refer to this link for nice explanation: https://medium.com/the-node-js-collection/what-you-should-know-to-really-understand-the-node-js-event-loop-and-its-metrics-c4907b19da4c

simplified event loop events

Azzieb answered 29/12, 2017 at 10:5 Comment(0)
E
4

I recommend you to check docs section dedicated for Loop to get better understanding. Some snippet taken from there:

We have two calls that are similar as far as users are concerned, but their names are confusing.

  • process.nextTick() fires immediately on the same phase

  • setImmediate() fires on the following iteration or 'tick' of the
    event loop

In essence, the names should be swapped. process.nextTick() fires more immediately than setImmediate(), but this is an artifact of the past which is unlikely to change.

Exoskeleton answered 21/7, 2018 at 17:4 Comment(0)
A
2

You should never break up CPU-heavy operation using process.nextTick()

You should never break up such work using process.nextTick(). Doing so will lead to a microtask queue that never empties—your application will be trapped in the same phase forever! -- Thomas Hunter. Distributed Systems with Node.js

Each phase of event loop contains several callback:

One phase of loop event

Once process.nextTick() fired, it will always stay in same phase.

Example

const nt_recursive = () => process.nextTick(nt_recursive);
nt_recursive(); // setInterval will never run

const si_recursive = () => setImmediate(si_recursive);
si_recursive(); // setInterval will run

setInterval(() => console.log('hi'), 10);

In this example, the setInterval() represents some asynchronous work that the application performs, such as responding to incoming HTTP requests.

Once the nt_recursive() function is run, the application ends up with a microtask queue that never empties and the asynchronous work never gets processed.

But the alternative version si_recursive() does not have the same side effect.

Making setImmediate() calls within a check phase adds callbacks to the next event loop iteration’s check phase queue, not the current phase’s queue.

Case might use process.nextTick

Make function process all asynchronously.

function foo(count, callback) {
  if (count <= 0) {
    return process.nextTick(() => callback(new TypeError('count > 0')));
  }
  myAsyncOperation(count, callback);
}

-- Thomas Hunter. Distributed Systems with Node.js

In this case, either using setImmediate() or process.nextTick() is okay; just make sure you don’t accidentally introduce recursion.

Adnopoz answered 31/3, 2021 at 1:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.