Given a function fn, an array of arguments args, and an interval time t, return a cancel function cancelFn.
After a delay of cancelTimeMs, the returned cancel function cancelFn will be invoked.
setTimeout(cancelFn, cancelTimeMs)
The function fn should be called with args immediately and then called again every t milliseconds until cancelFn is called at cancelTimeMs ms.
Example 1:
Input: fn = (x) => x * 2, args = [4], t = 35
Output:
[
{"time": 0, "returned": 8},
{"time": 35, "returned": 8},
{"time": 70, "returned": 8},
{"time": 105, "returned": 8},
{"time": 140, "returned": 8},
{"time": 175, "returned": 8}
]
Explanation:
const cancelTimeMs = 190;
const cancelFn = cancellable((x) => x * 2, [4], 35);
setTimeout(cancelFn, cancelTimeMs);
Every 35ms, fn(4) is called. Until t=190ms, then it is cancelled.
1st fn call is at 0ms. fn(4) returns 8.
2nd fn call is at 35ms. fn(4) returns 8.
3rd fn call is at 70ms. fn(4) returns 8.
4th fn call is at 105ms. fn(4) returns 8.
5th fn call is at 140ms. fn(4) returns 8.
6th fn call is at 175ms. fn(4) returns 8.
Cancelled at 190ms
Example 2:
Input: fn = (x1, x2) => (x1 * x2), args = [2, 5], t = 30
Output:
[
{"time": 0, "returned": 10},
{"time": 30, "returned": 10},
{"time": 60, "returned": 10},
{"time": 90, "returned": 10},
{"time": 120, "returned": 10},
{"time": 150, "returned": 10}
]
Explanation:
const cancelTimeMs = 165;
const cancelFn = cancellable((x1, x2) => (x1 * x2), [2, 5], 30)
setTimeout(cancelFn, cancelTimeMs)
Every 30ms, fn(2, 5) is called. Until t=165ms, then it is cancelled.
1st fn call is at 0ms
2nd fn call is at 30ms
3rd fn call is at 60ms
4th fn call is at 90ms
5th fn call is at 120ms
6th fn call is at 150ms
Cancelled at 165ms
Example 3:
Input: fn = (x1, x2, x3) => (x1 + x2 + x3), args = [5, 1, 3], t = 50
Output:
[
{"time": 0, "returned": 9},
{"time": 50, "returned": 9},
{"time": 100, "returned": 9},
{"time": 150, "returned": 9}
]
Explanation:
const cancelTimeMs = 180;
const cancelFn = cancellable((x1, x2, x3) => (x1 + x2 + x3), [5, 1, 3], 50)
setTimeout(cancelFn, cancelTimeMs)
Every 50ms, fn(5, 1, 3) is called. Until t=180ms, then it is cancelled.
1st fn call is at 0ms
2nd fn call is at 50ms
3rd fn call is at 100ms
4th fn call is at 150ms
Cancelled at 180ms
Constraints:
fn is a functionargs is a valid JSON array1 <= args.length <= 1030 <= t <= 10010 <= cancelTimeMs <= 500Problem Overview: You need to implement a utility that repeatedly executes a function at fixed time intervals and returns a cancellation function. The function runs immediately, then continues running every t milliseconds until the returned cancel function stops the interval.
Approach 1: Using setInterval and clearInterval (O(1) time, O(1) space)
This approach relies directly on JavaScript’s built-in timer APIs. First, call fn(...args) immediately to satisfy the requirement that the function runs at time 0. Then create a repeating timer with setInterval that invokes the same function every t milliseconds. Store the interval ID returned by setInterval. The cancel function simply calls clearInterval(intervalId), which stops all future executions.
The key insight is that setInterval already handles repeated scheduling internally, so you only manage the initial call and the cancellation handle. This keeps the implementation minimal and avoids manual timer management. Setup cost is constant because you create a single interval and return a closure referencing its ID. This is the most idiomatic solution when working with JavaScript timer APIs.
Approach 2: Manual Loop with setTimeout (O(1) time, O(1) space)
This approach simulates interval behavior using repeated setTimeout calls. Start by executing fn(...args) immediately. Then define a helper function that schedules the next execution with setTimeout. Each time the timeout fires, it runs the function and schedules another timeout.
To support cancellation, track a boolean flag or the timeout ID. When the cancel function runs, update the flag or call clearTimeout to prevent the next scheduled execution. This technique is common in asynchronous programming when you need finer control over scheduling behavior compared to setInterval. It also avoids overlapping executions if a task takes longer than the interval.
The recursive scheduling pattern is slightly more verbose but flexible. Developers often prefer it when precise timing control or dynamic delays are required.
Recommended for interviews: The setInterval and clearInterval solution is the expected answer. It shows you understand how browser and Node.js timer functions work and how closures retain the interval ID for cancellation. The manual setTimeout loop demonstrates deeper understanding of scheduling mechanics and event-loop behavior, which can be useful if interviewers ask about alternative implementations.
This approach leverages JavaScript's setInterval function to repeatedly invoke a function at specified intervals. The function is initially called immediately, then periodically every t milliseconds. A separate cancellation function is prepared using clearInterval to stop the repeated invocation when it's called after cancelTimeMs.
This implementation of the cancel function sets up an interval using setInterval. Initially, the function fn is called with the provided args. Then, every t milliseconds, the setInterval calls the function again. The clearInterval function is returned to stop further executions when called.
JavaScript
Time Complexity: O(1) per function call, ignoring the delay.
Space Complexity: O(1) as no additional data structures are used.
This approach uses recursive calls to setTimeout to simulate the behavior of setInterval. This gives finer control over each invocation of the function, and you can easily stop future calls by not setting further timeouts.
This code sets up a manual loop using setTimeout. The helper function callFn checks if the process is still active, invokes the function, and schedules the next invocation with setTimeout. The returned cancelFn sets the flag isActive to false, stopping further invocations.
JavaScript
Time Complexity: O(1) per function call, ignoring the delay.
Space Complexity: O(1) as only a boolean flag is used to manage state.
TypeScript
| Approach | Complexity |
|---|---|
| Approach 1: Using setInterval and clearInterval | Time Complexity: O(1) per function call, ignoring the delay. |
| Approach 2: Manual Loop with setTimeout | Time Complexity: O(1) per function call, ignoring the delay. |
| Default Approach | — |
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Using setInterval and clearInterval | O(1) setup | O(1) | Best general solution when implementing repeating timers in JavaScript |
| Manual Loop with setTimeout | O(1) setup | O(1) | Useful when you need finer control over scheduling or want to avoid overlapping executions |
Interval Cancellation | Leetcode 2725 | Promises and Time | 30 Days of JavaScript #javascript • Learn With Chirag • 2,465 views views
Watch 9 more video solutions →Practice Interval Cancellation with our built-in code editor and test cases.
Practice on FleetCodePractice this problem
Open in Editor