6

If you call setTimeout with a small or large negative number, the callback is run immediately, but with a medium-sized negative number, the callback is never run. Can someone explain this?

// Y and Z are printed, but not X

var x = -7677576503;
var y = -1000000000;
var z = -10000000000;

setTimeout(function () {
  console.log('x');
}, x);
setTimeout(function () {
  console.log('y');
}, y);
setTimeout(function () {
  console.log('z');
}, z);

JSFiddle version

(tested on Chromium 57.0.2987.98 and on Firefox 50.1.0)

TylerH
  • 20,799
  • 66
  • 75
  • 101
jcarpenter2
  • 5,312
  • 4
  • 22
  • 49
  • "Providing setTimeout a negative time will not always result in the callback function being called. This works in other browsers, but in Internet Explorer (8 or lower) you have to make sure any negative times are changed to zero." Are you sure? – Sterling Archer Apr 11 '17 at 23:57
  • Yes, I am sure. – jcarpenter2 Apr 11 '17 at 23:58
  • "According to the MDN reference, the specification requires that there is a minimum timeout. If you provide something less than this (HTML5 spec says 4ms) then the browser will just ignore your delay and use the minimum." Are you *really* sure? – Sterling Archer Apr 11 '17 at 23:58
  • 8
    even if it was not a duplicate, the question is still a bad fit here, since there really isn't any *practical* reason to do this in the first place. – Claies Apr 11 '17 at 23:58
  • simple solution...never pass negative numbers to setTimeout – charlietfl Apr 12 '17 at 00:03
  • I'm pretty sure I have an explanation to what is going on. I've voted to re-open, sorry for the cv. it stores as a 32-bit int internally, and when you convert the x value to a 32-bit int it is a very large timeout. setTimeout(fn,-1) fires immediately. all negatives (in chrome) do, as long as they are negative after the 32-bit int conversion... if this gets re-opened I'll post an answer. (to check, any bitwise operation will force a 32-bit int response. so `x | 0` will show you that positive result.) – rlemon Apr 12 '17 at 00:10
  • 8
    You don't need a practical reason to ask here, well-described theoretical questions are more than welcome. – ssube Apr 12 '17 at 00:31

1 Answers1

16

I think I have the answer.
according to MDN:

Browsers including Internet Explorer, Chrome, Safari, and Firefox store the delay as a 32-bit signed integer internally.

the browser is converting this value to a 32-bit signed int. so when it sees the values you've passed we can assume it is actually acting on the ones it converts to that type, and the ECMAScript specification says the return values from bitwise operations must be a 32-bit int.

Runtime Semantics: Evaluation The production A : A @ B, where @ is one of the bitwise operators in the productions above, is evaluated as follows:
... [snipped].
Return the result of applying the bitwise operator @ to lnum and rnum. The result is a signed 32 bit integer.

so if we put that together and test the values you've given:

x | 0 === 912358089, so the timeout will eventually be executed.. just in a while.
y | 0 === -1000000000, and the callback is fired immediately*.
z | 0 === -1410065408, still a negative, still fired immediately*.

*all tests done in chrome latest stable

you can test this with other negatives that would result in a positive when converted to a 32-bit signed int.
-7000000000 | 0 would result in 1589934592, and calling setTimeout(fn, -7000000000) doesn't appear to fire... today.

keeping in mind, this is my best guess at what is happening. good luck!

edit: thanks to Vivek Athalye, I think I have confirmation this is what is happening.

-4294967290000 | 0 === 6000, and if you run setTimeout(_ => console.log(1), -4294967290000) that fires in aprox. 6 seconds.

Community
  • 1
  • 1
rlemon
  • 17,518
  • 14
  • 92
  • 123