@@ -293,8 +293,11 @@ within an I/O cycle, independently of how many timers are present.
293
293
You may have noticed that ` process.nextTick() ` was not displayed in the
294
294
diagram, even though it's a part of the asynchronous API. This is because
295
295
` 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.
298
301
299
302
Looking back at our diagram, any time you call ` process.nextTick() ` in a
300
303
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()`
395
398
to allow the script to run to completion. This allows the user to set
396
399
any event handlers they want.
397
400
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
+
398
451
## ` process.nextTick() ` vs ` setImmediate() `
399
452
400
453
We have two calls that are similar as far as users are concerned, but
0 commit comments