If you're writing some code on a time-delayed-loop in JavaScript, there are 2 main options:

However, each has a potential downside:

  • setInterval schedules the code to run at the given interval, regardless of whether the last invocation has completed. This means that, if the function takes longer than the delay to execute, it'll schedule the next invocation before the current invocation is finished, which can lead to an avalanche of calls being piled on without bound if the functions continue to take equivalent amounts of time to run and aren't being delegated to truly concurrent threads.
  • Recursive setTimeout is a memory leak when run for unbounded time on Firefox and Chrome (though, interestingly: not in Safari et. al.!).

If you want "the best of both worlds": code that waits for invocation n to finish before starting invocation n+1, but does not leak memory on Firefox and Chrome, you'll have to take advantage of await to logically emulate the tail-recursion iteratively:

var [setRepeatedTimeout, clearRepeatedTimeout] = (() => {
	const asleep = (delay => new Promise(resolve => setTimeout(resolve, delay)));
	const repeatedTimeoutIntervals = [];

	function setRepeatedTimeout(f, delay, ...arguments) {
		//Like setInterval, but waits for an invocation to complete before scheduling the next one
	 	//(Bonus: supports both classic and async functions)
		const intervalID = repeatedTimeoutIntervals.push(delay) - 1;
		(async () => {
			await asleep(delay);
			while(intervalID in repeatedTimeoutIntervals) {
				await f(...arguments);
				await asleep(delay);
			}
		})();
		return intervalID;
	}

	function clearRepeatedTimeout(intervalID) {
		//Clears loops set by setInterval()
		delete repeatedTimeoutIntervals[intervalID];
	}

	return [setRepeatedTimeout, clearRepeatedTimeout];
})();

Leave a Reply

Your email address will not be published.

Warning: This site uses Akismet to filter spam. Until or unless I can find a suitable replacement anti-spam solution, this means that (per their indemnification document) all commenters' IP addresses will be sent to Automattic, Inc., who may choose to share such with 3rd parties.
If this is unacceptable to you, I highly recommend using an anonymous proxy or public Wi-Fi connection when commenting.