Intuition

  • This question requires a thorough understanding of Promises and async await.
  • Traditionally, I begin by setting a timeout, then if that timeout resolves, only then do I reject.
  • Otherwise, I can use async await as follows:
    • I require the callback itself to be async within the returned promise, i.e. async (resolve, reject)
    • Then, I await that result, and thereby resolve immediately after. I surround by try catch blocks as well.
    • this works since async is syntactic sugar which itself returns a promise, so I won’t have memory leaks. In other words, it is a promise inside a promise …

Code

Async Await

type Fn = (...params: any[]) => Promise<any>;
 
function timeLimit(fn: Fn, t: number): Fn {
    return async function(...args) {
        return new Promise(async (resolve, reject) => {
            setTimeout(() => reject("Time Limit Exceeded"), t);
 
            try {
                const res = await fn(...args)
                resolve(res);
            } catch (err) {
                reject(err);
            }
        })
    }
};

Traditional

type Fn = (...params: any[]) => Promise<any>;
 
function timeLimit(fn: Fn, t: number): Fn {
    return async function(...args) {
        return new Promise((resolve, reject) => {
            const id = setTimeout(() => reject("Time Limit Exceeded"), t);
            fn(...args)
            .then((res) => resolve(res))
            .catch((err) => reject(err))
            .finally(() => clearTimeout(id)); // Avoids memory leaks
        })
    }
};
 
/**
 * const limited = timeLimit((t) => new Promise(res => setTimeout(res, t)), 100);
 * limited(150).catch(console.log) // "Time Limit Exceeded" at t=100ms
 */

References