Using the following code, the counter reaches around 690 on my machine:
let counter = 0;
function doit() { counter++; setTimeout(doit, 0); }
setTimeout(() => {
console.log('Counter:', counter);
require('process').exit(0);
}, 1000);
setImmediate(doit);
Specifying a delay of 1 instead of 0 produces the same result. Changing "setTimeout(doit, 0)" to "setImmediate(doit)" results in the counter reaching around 580,000.
This occurs any time setTimeout is invoked with a delay argument that is zero.
setTimeout with a delay of 0 should not impose a 1-millisecond delay.
setTimeout imposes a minimum 1-millisecond delay.
No strong opinion here, but imo setting setTimeout(fn, 0) and setTimeout(fn) to just be setImmediate(fn) would a) make sense and b) be considered a breaking change.
@nodejs/timers
setTimeout 0 and undefined sounds like a programming error to me.
@devsnek According to https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout, setTimeout of zero is valid, and if the argument is not specified, it is assumed to be zero.
@fmela that's not true in any implementation I'm aware of. node apparently sets it to 1 and browsers have a minimum between 4-16ms.
@devsnek https://nodejs.org/api/timers.html#timers_settimeout_callback_delay_args states: "When delay is larger than 2147483647 or less than 1, the delay will be set to 1."
So this is intended behavior. I don't understand the justification for imposing a minimum 1-millisecond delay, though.
See https://github.com/nodejs/node-v0.x-archive/issues/593 and commit 7fc835afe362ebd30a0dbec81d3360bd24525222 - basically, it's because node wanted to mimic browser behavior.
Preempting the "why not 4 ms then?" question: because there's no frame rate in node.
The current behavior is quite intentional - I think there is an issue from before (other than the one Ben linked to) I recall having to align with this behavior in Sinon (I help maintain fake-timers).
@fmela and thank you for raising this issue and caring. We need more people to care about this, be engaged and contribute in timers and figure out what the correct behavior should be :] I appreciate it.
Closing, no follow-up.
To be clear, if at any point you wish to follow up @fmela we are happy to have a discussion here and figure out how to proceed.
Maybe @itayperry (who made a bunch of PRs to sinon) wants to pick up the docs PR part?
I would love to help in any way possible :)
@itayperry this needs a docs pr basically
Is the docs update needed for explaining the current built-in behavior, or should I do the pr only if an actual change was made?
Thank you @benjamingr. Sorry, I lost track of this issue.
My two cents: the considerations of browsers, which are user-facing and must maintain interactive responsiveness, are not concerns that should be shared by node. It's reasonable to impose a minimum delay in setTimeout when used in a browser in order to prevent a particular script or tab from causing CPU starvation and harming interactivity. In addition, it seems arbitrary that node enforces a 1-millisecond minimum (why that number and not .01, .1, or 10 milliseconds, for example)?
node already provides the setImmediate facility which makes it easy to use up all the CPU if used incorrectly (or even intentionally), so it's not clear why setTimeout should prevent the user from doing the same. Similarly, browsers don't provide setImmediate due to differing concerns; it isn't appropriate for interactive user-facing applications.
I feel that node's implementation should trust the developer when they give a timeout of zero. It's not hard to imagine unintended latency on the order of milliseconds being introduced in server-side node applications in which the developer hasn't realized that setTimeout(, 0) silently introduces one millisecond of latency each time it is used. Anecdotally, it came as a surprise to me.
Thanks for your consideration!
Most helpful comment
See https://github.com/nodejs/node-v0.x-archive/issues/593 and commit 7fc835afe362ebd30a0dbec81d3360bd24525222 - basically, it's because node wanted to mimic browser behavior.
Preempting the "why not 4 ms then?" question: because there's no frame rate in node.