đź‘»The Nightmarishly Dangerous Default Behavior of setInterval()

Randall - Jan 8 '22 - - Dev Community

So you want to send an HTTP request every delay number of milliseconds. Easy:

setInterval(
  () => {
    doRequest();
  },
  delay,
);
Enter fullscreen mode Exit fullscreen mode

But what if delay is undefined?

Here's where it gets scary. It turns out that if delay is undefined (or null, NaN, etc), it will default to 1 millisecond! Enjoy your DoS attack!

I've seen this issue affect production systems on two occasions now, and both instances caused significant damage and required emergency maintenance.

A Safer setInterval()

If you're concerned about this possibility, you can replace setInterval with safer code. Execute this code as early as possible during initialization:

const oldSetInterval = setInterval;
globalThis.setInterval = (callback, delay, ...rest) => {
  if (typeof delay !== 'number' || delay < 1 || delay > 2147483647) {
    throw new Error(`Invalid interval delay: ${delay}`);
  }

  return oldSetInterval(callback, delay, ...rest);
};
Enter fullscreen mode Exit fullscreen mode

This replaces the setInterval function globally with a wrapper that will throw an error if the delay argument is not a number or is outside of the valid range. No one likes errors, but it's usually better than accidentally making 1,000 requests per second!

Other Implementations of setInterval()

I've been talking about Node.js so far, and this behavior is clearly documented in the Node.js documentation.

But setInterval() is actually not part of the ECMAScript standard, and some of its details may vary across different JavaScript environments.

Here's some code you can execute to see what the default delay is in your environment:

let prev;
setInterval(() => {
  const cur = new Date().getTime();
  if (prev) {
    console.log(`MS elapsed: ${cur - prev}`);
  }

  prev = cur;
});
Enter fullscreen mode Exit fullscreen mode

It looks like in Firefox, the default delay is 16 ms, while in Chrome it's 4. While the Node.js documentation states that it's 1 ms, my testing suggests that it's very inconsistent in Node.js, much more so than in browsers. I'm not sure why.

setTimeout()

To be clear, yes, setTimeout() does have exactly the same issue.

Conclusion

I don't know who thought it was a good idea to default the delay to a low number. Throwing an error when delay is invalid sounds like a much better idea to me. But whatever, it is what it is! We just have to understand it and be careful.

. . . . . . . . . . . . . . . . . . . . . . .