Problem
Given a function fn, an array of arguments args, and a timeout t in milliseconds, return a cancelFn function.
The function fn should be executed after t milliseconds with the arguments from args. However, if the returned cancelFn is invoked before the timeout ends, fn should not be executed.
Example 1:
Input: (fn = (x) => x * 5), (args = [2]), (t = 20);
Output: [{ time: 20, returned: 10 }];
Example 2:
Input: (fn = (x) => x ** 2), (args = [2]), (t = 100);
Output: [];
My Approach
Initial Thoughts
This is essentially a timing and cancellation problem. We are dealing with delayed execution and possible interruption before the scheduled time.
Solution Strategy
- Immediately schedule
fn(...args)usingsetTimeout - Capture the timeout ID so we can cancel it later
- Return a
cancelFnthat can callclearTimeout(timeoutId) - If
cancelFnis called beforetms,fnwill never run - Otherwise,
fnexecutes aftertms as expected
Solution
JavaScript one-liner with closure:
var cancellable = function (fn, args, t) {
let timeoutID = setTimeout(() => fn(...args), t);
return () => clearTimeout(timeoutID);
};
Complexity Analysis
| Measure | Complexity |
|---|---|
| Time Complexity | O(1) |
| Space Complexity | O(1) |
setTimeoutandclearTimeoutare constant-time operations- No additional data structures are used
Key Insights & Learnings
- Closures are essential: The returned cancel function has access to
timeoutIDbecause it’s enclosed within the parent scope. - setTimeout returns a handle: This ID is required for canceling the timeout.
- Immediate scheduling: The
setTimeoutshould run immediately whencancellableis called, not whencancelFnis called. - Timing precision: The actual execution time may vary slightly due to the JS event loop, so always use
performance.now()orDate.now()when testing precision.
Common Pitfalls
- Scheduling the timeout inside the returned function, which delays scheduling until it’s too late.
- Forgetting that
clearTimeoutonly works before the timer has fired. - Forgetting to pass arguments correctly: use the spread operator
fn(...args).
Use Case / When to Use
- Debouncing or throttling user input
- Canceling expensive operations (e.g., API calls) if no longer needed
- Implementing timeout logic in games or UI behavior
Notes
Time Taken: ~5 min Confidence Level: Solid What I’d Do Differently:
- Experiment with
Promise-based timeouts to practice - Wrap this logic in a reusable utility function for async workflows
Follow-up Questions
- What if we wanted
cancelFnto return a boolean indicating whether the cancellation was successful? - How could we log whether
fnwas actually executed? - Could this be refactored to support multiple scheduled executions with separate cancels?
This problem reinforced my understanding of how closures and timers work in JavaScript, especially for delayed and cancellable executions.