Last active
April 15, 2022 16:22
-
-
Save ryanvazquez/3f37d1a23ad1e0d5dc7df449ad96079a to your computer and use it in GitHub Desktop.
Simple utility for creating variadic intervals
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// setIntervalTimeout sits somewhere between setTimeout and setInterval, | |
// allowing a finite or infinite number of fixed or variadic intervals. | |
// Returns a cleanup function to clear the current timeout. | |
// Note: IE does not support generators. | |
function setIntervalTimeout(callback, iterable, ...args){ | |
if (!(Symbol.iterator in iterable)){ | |
throw new Error(`Expected valid iterable. Received: ${iterable}.`); | |
} | |
let timeout = null; | |
function iterate(itr){ | |
const next = itr.next(); | |
if (!next.done){ | |
timeout = setTimeout(() => { | |
callback(...args); | |
iterate(itr); | |
}, next.value); | |
} | |
} | |
iterate(iterable[Symbol.iterator]()); | |
return () => clearTimeout(timeout); | |
} | |
// Bad: | |
setIntervalTimeout(console.log, { time: 1000 }, "Hello, world!"); // objects are not iterables | |
setIntervalTimeout(console.log, 1000, "Hello, world!"); // numbers are not iterables, use setTimeout instead. | |
// Weird: | |
// strings are iterable and valid timeout values. Will execute once, immediately, for each char. | |
setIntervalTimeout(console.log, "foobar", "Hello, world!"); | |
// arrays are iterable but values are invalid timeout values. Will execute once, immediately, for each element. | |
setIntervalTimeout(console.log, [{foo: "foo"}], "Hello, world!"); | |
// Unlikely - Maps, Sets, WeakMaps, etc: | |
setIntervalTimeout(console.log, new Map([1000, 1000]), "Hello, world!"); | |
setIntervalTimeout(console.log, new Set([1000, 1000]), "Hello, world!"); | |
setIntervalTimeout(console.log, new WeakMap([1000, 1000]), "Hello, world!"); | |
// Arrays: | |
// Most of the time you will use an array of numbers. | |
setIntervalTimeout(console.log, [500], "Hello, world!"); // identical to setTimeout | |
setIntervalTimeout(console.log, [500, 500], "Hello, world!"); // identical to cancelling setInterval after two fixed intervals. | |
setIntervalTimeout(console.log, [500, 750, 1000], "Hello, world!"); // Three custom intervals. | |
// ...but you can technically use strings since JS will coerce them to numbers but don't do this. | |
setIntervalTimeout(console.log, ["500", "500"], "Hello, world!"); | |
// Iterators: | |
// Useful for creating intervals programatically | |
const exponentialBackoff = function* generateFib(){ | |
let a = 0, b = 1; | |
while (Number.isSafeInteger(a + b)){ | |
const c = a + b; | |
a = b, b = c; | |
yield c; | |
} | |
}() //intervals increase exponentially - useful when retrying a failed network request | |
const linearBackoff = function* generateLinear(ms, start = 0){ | |
let next = start; | |
while (Number.isSafeInteger(next + ms)) { | |
next += ms; | |
yield next; | |
} | |
}(500); // intervals increase linearly - useful for less aggressive backoffs | |
setIntervalTimeout(console.log, exponentialBackoff, "Retrying IMMEDIATELY RIGHT NOW..."); | |
setIntervalTimeout(console.log, linearBackoff, "Retrying at a leisurely pace..."); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment