From 6a7c0e636820b891089326fa2a6290a342590190 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Wed, 30 Apr 2025 13:57:51 -0700 Subject: [PATCH] perf(clock): use setImmediate for autoTick macrotask in Node When called within an I/O cycle, `setImmediate` is generally faster because it is designed to execute immediately after the current I/O event completes, whereas `setTimeout(0)` gets placed in the timers queue and might be subject to delays. > The main advantage to using setImmediate() over setTimeout() is setImmediate() > will always be executed before any timers if scheduled within an I/O cycle, > independently of how many timers are present. * https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#setimmediate-vs-settimeout * https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#poll * https://nodejs.org/en/learn/asynchronous-work/understanding-setimmediate --- spec/core/ClockSpec.js | 14 ++++++++++++++ src/core/Clock.js | 6 ++++++ 2 files changed, 20 insertions(+) diff --git a/spec/core/ClockSpec.js b/spec/core/ClockSpec.js index dbdbbfd6..529e744a 100644 --- a/spec/core/ClockSpec.js +++ b/spec/core/ClockSpec.js @@ -715,6 +715,20 @@ describe('Clock (acceptance)', function() { clock.uninstall(); }); + it('flushes microtask queue between macrotasks', async () => { + const log = []; + await new Promise(r => clock.setTimeout(r, 10)).then(() => { + log.push(1); + Promise.resolve().then(() => log.push(2)); + Promise.resolve().then(() => log.push(3)); + }); + await new Promise(r => clock.setTimeout(r, 10)).then(() => { + log.push(4); + Promise.resolve().then(() => log.push(5)); + }); + expect(log).toEqual([1, 2, 3, 4, 5]); + }); + it('can run setTimeouts/setIntervals asynchronously', function() { const recurring = jasmine.createSpy('recurring'), fn1 = jasmine.createSpy('fn1'), diff --git a/src/core/Clock.js b/src/core/Clock.js index dc2378d6..552aff67 100644 --- a/src/core/Clock.js +++ b/src/core/Clock.js @@ -222,6 +222,12 @@ callbacks to execute _before_ running the next one. // // @return {!Promise} async function newMacrotask() { + if (NODE_JS) { + // setImmediate is generally faster than setTimeout in Node + // https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#setimmediate-vs-settimeout + return new Promise(resolve => void setImmediate(resolve)); + } + // MessageChannel ensures that setTimeout is not throttled to 4ms. // https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified // https://stackblitz.com/edit/stackblitz-starters-qtlpcc