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:
fn completes within the time limit of t milliseconds, the time limited function should resolve with the result.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 <= 100 <= t <= 1000fn returns a promiseThe key idea in #2637 Promise Time Limit is to create a wrapper around an asynchronous function so that it rejects if execution exceeds a given time limit. The challenge is coordinating the original promise with a timer without blocking the event loop.
A common strategy is to run two asynchronous operations simultaneously: the original function call and a timeout mechanism. Using Promise.race, whichever promise settles first determines the result. If the function resolves or rejects before the timer, its result is returned normally. If the timer finishes first, the wrapper rejects with a "Time Limit Exceeded" error.
This approach leverages JavaScript's event-driven model and keeps the implementation concise. Since the wrapper simply orchestrates promises and timers, the time complexity is constant per invocation, and the space usage remains minimal, making it efficient for repeated async calls.
| Approach | Time Complexity | Space Complexity |
|---|---|---|
| Promise.race with timeout timer | O(1) | O(1) |
| Manual timer with Promise wrapper | O(1) | O(1) |
NeetCodeIO
Use these hints if you're stuck. Try solving on your own first.
You can return a copy of a function with: function outerFunction(fn) { return function innerFunction(...params) { return fn(...params); }; }
Inside the inner function, you will need to return a new Promise.
You can create a new promise like: new Promise((resolve, reject) => {}).
You can execute code with a delay with "setTimeout(fn, delay)"
To reject a promise after a delay, "setTimeout(() => reject('err'), delay)"
You can resolve and reject when the passed promise resolves or rejects with: "fn(...params).then(resolve).catch(reject)"
This approach leverages the Promise.race() function to race between the original asynchronous function and a timeout promise. If the function executes within the time limit, it resolves with the function's result. Otherwise, the timeout promise rejects with "Time Limit Exceeded."
Time Complexity: O(1) — The best and worst case are consistent as it relies on how quickly the promise in fn resolves.
Space Complexity: O(1) — A constant amount of additional space is used.
1function timeLimit(fn, t) {
2 return async function (...args) {
3 const timeoutPromise = new Promise((_, reject) =>
4 setTimeout(() => reject('Time Limit Exceeded'), t)
5 );
6 return Promise.race([fn(...args), timeoutPromise]);
7 };
8}This code wraps the original function within an asynchronous function that introduces a race condition between the function and a timeout duration using Promise.race(). If fn completes within t milliseconds, its resolved value is returned; otherwise, it gets rejected.
This approach manually sets a timeout before invoking the asynchronous function and uses the result as a flag to determine if it has been reached. If the function attempt completes before the timeout, it clears the timeout; otherwise, it signals a reject using the timeout handler.
Time Complexity: O(1) — Performance does not vary significantly with time limit or function complexity.
Space Complexity: O(1) — Additional space is minimally affected by handling timeouts.
1function timeLimit(fn, t) {
2 return async functionThis approach utilizes Promise.race to handle the timeout. Promise.race allows us to race the original function's promise against a new promise that rejects after the specified duration t. If the original function resolves before the timeout, the new function resolves successfully. Otherwise, it rejects with a timeout error.
Time Complexity: O(1) — Relying on JavaScript's Promise system, where each promise has a negligible overhead.
Space Complexity: O(1) — Constant space used primarily for the setTimeout and the Promise itself.
1function timeLimit(fn, t) {
2 return async This approach manually controls the timeout by creating a new promise that listens to both the resolution of fn and the timeout. By tracking which occurs first, we either resolve or reject appropriately. This approach sorts out through manual management of completion signals.
Time Complexity: O(1) — The complexity is based on the constant time operations, with the Promise system operation being negligible.
Space Complexity: O(1) — Uses constant space for the timeout tracking and signal flags.
1function timeLimit(fn, t) {
2 return async function
Watch expert explanations and walkthroughs
Jot down your thoughts, approach, and key learnings
Yes, variations of async control problems are common in frontend and JavaScript-heavy interviews at large tech companies. They test understanding of promises, event loops, and asynchronous patterns used in real-world applications.
The most common approach is to use Promise.race between the original asynchronous function and a timeout promise. The promise that settles first determines the result. If the timeout resolves first, the wrapper rejects with a "Time Limit Exceeded" error.
Promise.race allows multiple promises to run concurrently and resolves or rejects with the first one that settles. This makes it ideal for implementing timeouts because the timer and the main async function can compete to determine the final outcome.
This problem mainly relies on JavaScript's asynchronous programming model rather than traditional data structures. Concepts such as promises, timers (setTimeout), and Promise.race are key to controlling execution timing.
The code demonstrates a manual timeout mechanism coupled with Promise.race(). It introduces a timeout and clears it in finally to ensure the resource management is maintained cleanly. Upon completion of the main promise or timeout, it uses clearTimeout to avoid any further actions on the timer.
The function timeLimit wraps the original asynchronous function fn. It returns an asynchronous function that races fn(...args) with a timeout promise. The timeout promise calls setTimeout, which rejects after t milliseconds. If fn(...args) resolves before the timeout, the Promise.race resolves; otherwise, it rejects.
The function timeLimit creates and returns a new promise containing the logic to handle both the execution of fn and the timeout. It uses a flag isResolved to ensure that only the first completion (either the resolution of the fn function or the setTimeout) is applied. If the fn function resolves or rejects before the timeout, it handles those outcomes. Otherwise, it eventually triggers the timeout rejection.