From e7af9830e98fcda7e7a11e0b13b667163cc8c940 Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Thu, 13 Sep 2018 07:35:15 -0700 Subject: timers: run nextTicks after each immediate and timer In order to better match the browser behaviour, run nextTicks (and subsequently the microtask queue) after each individual Timer and Immediate, rather than after the whole list is processed. The current behaviour is somewhat of a performance micro-optimization and also partly dictated by how timer handles were implemented. PR-URL: https://github.com/nodejs/node/pull/22842 Fixes: https://github.com/nodejs/node/issues/22257 Reviewed-By: Anna Henningsen Reviewed-By: Gus Caplan Reviewed-By: Jeremiah Senkpiel Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater --- benchmark/timers/timers-timeout-nexttick.js | 36 +++++ doc/api/process.md | 18 +-- lib/internal/process/next_tick.js | 11 +- lib/timers.js | 175 +++++++++------------ test/message/events_unhandled_error_nexttick.out | 1 + test/message/nexttick_throw.out | 1 + test/message/stdin_messages.out | 8 +- test/message/timeout_throw.out | 2 - test/message/unhandled_promise_trace_warnings.out | 4 +- test/parallel/test-process-fatal-exception-tick.js | 26 --- test/parallel/test-repl-underscore.js | 2 - test/parallel/test-timers-immediate-queue.js | 9 +- test/parallel/test-timers-next-tick.js | 27 +++- 13 files changed, 162 insertions(+), 158 deletions(-) create mode 100644 benchmark/timers/timers-timeout-nexttick.js delete mode 100644 test/parallel/test-process-fatal-exception-tick.js diff --git a/benchmark/timers/timers-timeout-nexttick.js b/benchmark/timers/timers-timeout-nexttick.js new file mode 100644 index 0000000000..781e505ae4 --- /dev/null +++ b/benchmark/timers/timers-timeout-nexttick.js @@ -0,0 +1,36 @@ +'use strict'; +const common = require('../common.js'); + +// The following benchmark measures setting up n * 1e6 timeouts, +// as well as scheduling a next tick from each timeout. Those +// then get executed on the next uv tick. + +const bench = common.createBenchmark(main, { + n: [5e4, 5e6], +}); + +function main({ n }) { + let count = 0; + + // Function tracking on the hidden class in V8 can cause misleading + // results in this benchmark if only a single function is used — + // alternate between two functions for a fairer benchmark. + + function cb() { + process.nextTick(counter); + } + function cb2() { + process.nextTick(counter); + } + function counter() { + count++; + if (count === n) + bench.end(n); + } + + for (var i = 0; i < n; i++) { + setTimeout(i % 2 ? cb : cb2, 1); + } + + bench.start(); +} diff --git a/doc/api/process.md b/doc/api/process.md index 2eac116058..3907271bbf 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -1461,13 +1461,11 @@ changes: * `callback` {Function} * `...args` {any} Additional arguments to pass when invoking the `callback` -The `process.nextTick()` method adds the `callback` to the "next tick queue". -Once the current turn of the event loop turn runs to completion, all callbacks -currently in the next tick queue will be called. - -This is *not* a simple alias to [`setTimeout(fn, 0)`][]. It is much more -efficient. It runs before any additional I/O events (including -timers) fire in subsequent ticks of the event loop. +`process.nextTick()` adds `callback` to the "next tick queue". This queue is +fully drained after the current operation on the JavaScript stack runs to +completion and before the event loop is allowed to continue. As a result, it's +possible to create an infinite loop if one were to recursively call +`process.nextTick()`. ```js console.log('start'); @@ -1542,11 +1540,6 @@ function definitelyAsync(arg, cb) { } ``` -The next tick queue is completely drained on each pass of the event loop -**before** additional I/O is processed. As a result, recursively setting -`nextTick()` callbacks will block any I/O from happening, just like a -`while(true);` loop. - ## process.noDeprecation