Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions doc/api/timers.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,12 @@ Node.js makes no guarantees about the exact timing of when callbacks will fire,
nor of their ordering. The callback will be called as close as possible to the
time specified.

When `delay` is larger than `2147483647` or less than `1` or `NaN`, the `delay`
will be set to `1`. Non-integer delays are truncated to an integer.
When `delay` is larger than `2147483647`, a negative number, or `NaN`, the
`delay` will be set to `1`. A delay of `0` (or a positive sub-millisecond
value, which is truncated to `0`) schedules the callback for the timers phase
of a subsequent event loop turn, without the `1` ms minimum delay. It does not
run within the current turn; the callback is invoked during a later iteration
of the event loop. Non-integer delays are truncated to an integer.

If `callback` is not a function, a [`TypeError`][] will be thrown.

Expand Down
12 changes: 11 additions & 1 deletion lib/internal/timers.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,17 @@ class Timeout {
'\nTimeout duration was set to 1.',
'TimeoutNaNWarning');
}
after = 1; // Schedule on next tick, follows browser behavior

// setTimeout() accepts a delay of 0 or a positive sub-millisecond
// delay, which is truncated to 0 by insert() and thus scheduled for a
// subsequent timers phase of the event loop without the 1 ms minimum,
// matching browsers (it still does not run within the current turn).
// Every other invalid delay (and every setInterval() delay below 1 ms,
// so it does not fire as fast as the event loop allows) is still
// clamped to 1 ms.
if (isRepeat || after < 0 || NumberIsNaN(after) || after > TIMEOUT_MAX) {
after = 1; // Schedule on next tick, follows browser behavior
}
}

this._idleTimeout = after;
Expand Down
25 changes: 25 additions & 0 deletions test/parallel/test-timers-zero-delay-ordering.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict';
const common = require('../common');
const assert = require('assert');

// setTimeout with a delay of 0 should schedule the callback as soon as
// possible, so that it runs before a timer scheduled with a 1 ms delay.
// See https://github.com/nodejs/node/issues/46596

const order = [];

setTimeout(common.mustCall(() => order.push('one')), 1);
setTimeout(common.mustCall(() => order.push('zero')), 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the CI test failure.. was worried about this. The "as soon as possible" means that it's going to race against non-zero but very short deadline timers in some cases. Think we need to put some exact timing semantics around it.

[
+   'one',
    'zero',
-   'one'
  ]


setTimeout(common.mustCall(() => {
assert.deepStrictEqual(order, ['zero', 'one']);
}), 2);

// A zero-millisecond delay must still be allowed for the promisified variant.
let resolved;
const p = require('node:timers/promises').setTimeout(0);
p.then(common.mustCall(() => { resolved = true; }));

setTimeout(common.mustCall(() => {
assert.strictEqual(resolved, true);
}), 2);
4 changes: 2 additions & 2 deletions test/parallel/test-timers.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ inputs.forEach((value, index) => {
}, value);
});

// All values in inputs array coerce to 1 ms. Therefore, they should all run
// before a timer set here for 2 ms.
// All values in inputs array coerce to a short delay (0 ms or 1 ms).
// Therefore, they should all run before a timer set here for 2 ms.

setTimeout(common.mustCall(() => {
// Assert that all other timers have run
Expand Down
Loading