later icon indicating copy to clipboard operation
later copied to clipboard

Minimum time to fire setTimeout should be documented

Open maarten-t opened this issue 8 years ago • 2 comments

I just discovered that when doing a later.setTimeout, it skips the first occurrence when it is within 1 second from now. Shouldn't this behaviour (see settimeout.js line 37) be documented?

Being dependent on later.setTimeout, later.setTimeout is affected as well. I guess the skipping of occurrences can lead to even more unexpected results there.

What's the reason behind the minimum delay?

maarten-t avatar Apr 26 '16 14:04 maarten-t

The reason appears to be that it's a fudge factor to combat the issue with the function executing earlier than expected: https://github.com/bunkat/later/issues/90#issuecomment-101754640

However, sometimes 1 second is not enough, causing the function to execute twice: https://github.com/bunkat/later/issues/90 https://github.com/bunkat/later/issues/99 https://github.com/bunkat/later/issues/146 https://github.com/bunkat/later/issues/81

MaxMem avatar Apr 26 '16 15:04 MaxMem

Ah, I see! A more robust solution, in my opinion, would be to make sure the timer callback isn't called prematurely. Wrapping setTimeout with something analogous to this would help:

var MAX_DELAY = 2147483647;
function setRobustTimer(fn, targetTimestamp) {
    var delay = targetTimestamp - Date.now();
    if (delay < 0) delay = 0; // Just to be sure, it might not be needed
    if (delay > MAX_DELAY) delay = MAX_DELAY; // To solve issue #162 as well
    setTimeout(function () {
        var now = Date.now();
        if (now >= targetTimestamp) {
            fn();
        } else {
            // Oops, too early! Let's add some additional delay
            setRobustTimer(fn, targetTimestamp);
        }
    }, delay);
}

This snippet is just to explain what I mean, of course. It doesn't include any logic to clear the timer. As a bonus it solves issue #162 as well. Unfortunately it doesn't solve the issue of executing the later.setInterval callback to few times because the timer triggers too late.

An alternative solution is to avoid the use of Date.now() in later.setTimeout. When the caller (later.setInterval in our case) supplies the value for now it can use the time it was supposed to trigger instead of the time it actually did. That way it doesn't matter whether it triggered too early or too late, it won't execute the callback too many or too few times. (I hope I'm explaining it clearly)

maarten-t avatar Apr 26 '16 16:04 maarten-t