Skip to content

Commit b440166

Browse files
OmarDarwishMaledong
authored and
Maledong
committed
Clarify when nextTick queue is processed (#1804)
It was a little unclear for me when the nextTickQueue is processed (see this StackOverflow question). After some experimentation and more googling, re-write to make it more clear.
1 parent 1d445b8 commit b440166

File tree

1 file changed

+55
-2
lines changed

1 file changed

+55
-2
lines changed

locale/en/docs/guides/event-loop-timers-and-nexttick.md

+55-2
Original file line numberDiff line numberDiff line change
@@ -293,8 +293,11 @@ within an I/O cycle, independently of how many timers are present.
293293
You may have noticed that `process.nextTick()` was not displayed in the
294294
diagram, even though it's a part of the asynchronous API. This is because
295295
`process.nextTick()` is not technically part of the event loop. Instead,
296-
the `nextTickQueue` will be processed after the current operation
297-
completes, regardless of the current phase of the event loop.
296+
the `nextTickQueue` will be processed after the current operation is
297+
completed, regardless of the current phase of the event loop. Here,
298+
an *operation* is defined as a transition from the
299+
underlying C/C++ handler, and handling the JavaScript that needs to be
300+
executed.
298301

299302
Looking back at our diagram, any time you call `process.nextTick()` in a
300303
given phase, all callbacks passed to `process.nextTick()` will be
@@ -395,6 +398,56 @@ To get around this, the `'listening'` event is queued in a `nextTick()`
395398
to allow the script to run to completion. This allows the user to set
396399
any event handlers they want.
397400

401+
### Deduplication
402+
403+
For the `timers` and `check` phases, there is a single transition
404+
between C to JavaScript for multiple immediates and timers. This deduplication
405+
is a form of optimization, which may produce some unexpected side effects.
406+
Take this code snippet as an example:
407+
408+
```js
409+
// dedup.js
410+
const foo = [1, 2];
411+
const bar = ['a', 'b'];
412+
413+
foo.forEach(num => {
414+
setImmediate(() => {
415+
console.log('setImmediate', num);
416+
bar.forEach(char => {
417+
process.nextTick(() => {
418+
console.log('process.nextTick', char);
419+
});
420+
});
421+
});
422+
});
423+
```
424+
```bash
425+
$ node dedup.js
426+
setImmediate 1
427+
setImmediate 2
428+
process.nextTick a
429+
process.nextTick b
430+
process.nextTick a
431+
process.nextTick b
432+
```
433+
434+
The main thread adds two `setImmediate()` events, which when processed
435+
will add two `process.nextTick()` events. When the event loop reaches
436+
the `check` phase, it sees that there are currently two events created by
437+
`setImmediate()`. The first event is grabbed and processed, which prints
438+
and adds two events to the `nextTickQueue`.
439+
440+
Because of deduplication, the event loop does not transition back to the
441+
C/C++ layer to check if there are items in the `nextTickQueue` immediately. It
442+
instead continues to process any remaining `setImmediate()` events, of which
443+
one currently remains. After processing this event, two more events are
444+
added to the `nextTickQueue` for a total of four events.
445+
446+
At this point, all previously added `setImmediate()` events have been processed.
447+
The `nextTickQueue` is now checked, and events are processed in FIFO order. When
448+
this `nextTickQueue` is emptied, the event loop considers all operations to have
449+
been completed for the current phase and transitions to the next phase.
450+
398451
## `process.nextTick()` vs `setImmediate()`
399452

400453
We have two calls that are similar as far as users are concerned, but

0 commit comments

Comments
 (0)