[Leetcode] JS 30 Promise Time Limit

[Leetcode] JS 30 Promise Time Limit

Question

Given an asynchronous function fn and a time t in milliseconds, return a new time limited version of the input function. fn takes arguments provided to the time limited function.

The time limited function should follow these rules:

  • If the fn completes within the time limit of t milliseconds, the time limited function should resolve with the result.
  • If the execution of the fn exceeds the time limit, the time limited function should reject with the string "Time Limit Exceeded".

Example 1:

Input:
fn = async (n) => {
  await new Promise(res => setTimeout(res, 100));
  return n * n;
}
inputs = [5]
t = 50
Output: {"rejected":"Time Limit Exceeded","time":50}
Explanation:
const limited = timeLimit(fn, t)
const start = performance.now()
let result;
try {
   const res = await limited(...inputs)
   result = {"resolved": res, "time": Math.floor(performance.now() - start)};
} catch (err) {
   result = {"rejected": err, "time": Math.floor(performance.now() - start)};
}
console.log(result) // Output

The provided function is set to resolve after 100ms. However, the time limit is set to 50ms. It rejects at t=50ms because the time limit was reached.

Example 2:

Input:
fn = async (n) => {
  await new Promise(res => setTimeout(res, 100));
  return n * n;
}
inputs = [5]
t = 150
Output: {"resolved":25,"time":100}
Explanation:
The function resolved 5 * 5 = 25 at t=100ms. The time limit is never reached.

Example 3:

Input:
fn = async (a, b) => {
  await new Promise(res => setTimeout(res, 120));
  return a + b;
}
inputs = [5,10]
t = 150
Output: {"resolved":15,"time":120}
Explanation:
The function resolved 5 + 10 = 15 at t=120ms. The time limit is never reached.

Example 4:

Input:
fn = async () => {
  throw "Error";
}
inputs = []
t = 1000
Output: {"rejected":"Error","time":0}
Explanation:
The function immediately throws an error.

Constraints:

  • 0 <= inputs.length <= 10
  • 0 <= t <= 1000
  • fn returns a promise

My Solution

/**
 * @param {Function} fn
 * @param {number} t
 * @return {Function}
 */
var timeLimit = function(fn, t) {
  return async function(...args) {
    // make below teo Promises race to ruturn the one that runs faster
    return Promise.race([
      fn(...args),
      new Promise((_,reject) => {setTimeout(() => {reject("Time Limit Exceed")},t)})
    ]
    )
  }
};

const limited = timeLimit((t) => new Promise(res => setTimeout(res, t)), 100);
limited(150).catch(console.log) // "Time Limit Exceeded" at t=100ms

Key Takeaways

setTimeout is not a Promise

setTimeout is not a Promise , so we can’t add await at the front of setTimeout .

In my initial approach to solving this question, I made a mistake as below. This is incorrect since setTimeout is not a Promise, but a function that schedules a callback to run after a specified time. It returns a numerical timeout ID, not a Promise, so using await in front of setTimeout has no effect. If we want to use await , the correct way is to wrap setTimeout inside a Promise . By wrapping setTimeout in a Promise, we can await it, effectively delaying execution within an async function.

// ❌ Incorrect: setTimeout does not return a Promise
var timeLimit = function(fn, t) {
  return async function(...args) {
    return await setTimeout(() => {fn(...args);}, t)
  }
};

// ✅ Correct: Wrap setTimeout inside a Promise
fn = async (n) => { 
  await new Promise(res => setTimeout(res, 100)); 
  return n * n; 
}

Promise.race([promise1, promise2, ...])

Promise.race([promise1, promise2, ...]) returns the first Promise to either resolve or reject.

Example:

const p1 = new Promise((resolve) => setTimeout(() => resolve("🚀 p1 finished"), 3000));
const p2 = new Promise((resolve) => setTimeout(() => resolve("🔥 p2 finished"), 1000));
const p3 = new Promise((resolve) => setTimeout(() => resolve("⚡ p3 finished"), 2000));

Promise.race([p1, p2, p3]).then(console.log); //🔥 p2 finished

💡
Promise.race is commonly used to enforce a timeout on API requests by racing the API call against a timeout Promise .

Example:

function fetchWithTimeout(url, timeout) {
  return Promise.race([
    fetch(url), // our API fetch request
    new Promise((_, reject) => setTimeout(() => reject(new Error("Request Timeout")), timeout))
  ]);
}

// we set 3s limit timeout
fetchWithTimeout("https://jsonplaceholder.typicode.com/todos/1", 3000)
  .then(response => response.json())
  .then(console.log)
  .catch(console.error);