Timer functions in JS

PUBLISHED ON: Sunday, Aug 6, 2023

LAST UPDATED ON: Monday, Aug 14, 2023

Asynchronous version of setTimeout()


/**
 * do something
 * sleep(2000);
 * do another thing
 */
export default async function sleep(duration: number): Promise<any> {
  return new Promise((resolve) => {
    setTimeout(resolve, duration);
  });
}

setCancellableInterval()


export default function setCancellableInterval(
  callback: Function,
  delay?: number,
  ...args: Array<any>
): () => void {
  const id = setInterval(() => {
    callback(...args);
  }, delay);
  return () => {
    clearInterval(id);
  };
}

setCancellableTimeout()


export default function setCancellableTimeout(
  callback: Function,
  delay?: number,
  ...args: Array<any>
): () => void {
  const id = setTimeout(() => {
    callback(...args);
  }, delay);

  return () => {
    clearTimeout(id);
  };
}

setTimeout() polyfill


const timers: Record<number, { callback: Function, delay: number }> = {};

function generateID() {
	return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
}

function generateNewID() {
	let id = generateID();

	while (id in timers) {
		id = generateID();
	}

	return id;
}

function _clearTimeout(id: number) {
	if (id in timers) {
		delete timers[id];
	}
}

function check() {
	if (Object.keys(timers).length <= 0) {
		return;
	}

	for (let timerID in timers) {
		const { callback, delay } = timers[timerID];

		if (Date.now() > delay) {
			callback();
			_clearTimeout(+timerID);
		}
	}

	requestIdleCallback(check);
}

function _setTimeout(
	callback: Function,
	delay?: number,
	...args: any[]
) {
	if (typeof callback !== "function") {
		throw new Error("callback should be a function");
	}

	const timerID = generateNewID();

	timers[timerID] = {
		callback: () => callback(...args),
		delay: Date.now() + (delay || 0),
	}

	if (Object.keys(timers).length === 1) {
		requestIdleCallback(check);
	}

  return timerID;
}


 window.requestIdleCallback

The window.requestIdleCallback() method queues a function to be called during a browser's idle periods. This enables developers to perform background and low priority work on the main event loop, without impacting latency-critical events such as animation and input response.

Functions are generally called in first-in-first-out order; however, callbacks which have a timeout specified may be called out-of-order if necessary in order to run them before the timeout elapses.

setInterval() polyfill


let intervals: Record<number, number> = {};

function _clearInterval(intervalID: number) {
	if (intervalID in intervals) {
		delete intervals[intervalID];
	}
}

function _setInterval(
	callback: Function,
	delay?: number,
	...args: any[]
) {
	const id = generateNewID();

	function repeat() {
		intervals[id] = _setTimeout(() => {
			callback(...args);

			if (id in intervals) {
				_clearInterval(intervals[id]);
				repeat();
			}
		}, delay);
	}

	repeat();

	return id;
}