@@ -293,8 +293,11 @@ within an I/O cycle, independently of how many timers are present.
293293You may have noticed that ` process.nextTick() ` was not displayed in the
294294diagram, 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
299302Looking back at our diagram, any time you call ` process.nextTick() ` in a
300303given 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()`
395398to allow the script to run to completion. This allows the user to set
396399any 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
400453We have two calls that are similar as far as users are concerned, but
0 commit comments