Difficulty: Easy | Acceptance: 89.50% | Paid: No Topics: N/A
Given a function fn, an array of arguments args, and a timeout t in milliseconds, return a cancel function cancelFn.
After a delay of t, fn should be called with args passed to it.
The cancelTimeFn function should clear the scheduled timeout.
The following is the example of how the cancelTimeFn will be used:
const result = []
const fn = (x) => x * 5
const args = [2], t = 20, cancelTimeMs = 50
const start = performance.now()
const log = (...argsArr) => {
const diff = Math.floor(performance.now() - start)
result.push({time: diff, returned: fn(...argsArr)})
}
const cancel = cancellable(log, args, t)
const cancelT = setTimeout(() => {
cancel()
}, cancelTimeMs)
setTimeout(() => {
clearTimeout(cancelT)
}, cancelTimeMs + t + 100)
- Table of Contents
- Examples
- Constraints
- setTimeout and clearTimeout
- Promise with Cancellation Flag
- Async/Await with Cancellation
Examples
Example 1
Input:
fn = (x) => x * 5, args = [2], t = 20
Output:
[{"time":20,"returned":10}]
Explanation:
const cancelTimeMs = 50;
const cancel = cancellable(fn, args, t);
setTimeout(cancel, cancelTimeMs);
The cancellation was scheduled to occur after 50ms, but the fn was executed at 20ms, so the cancellation had no effect.
Example 2
Input:
fn = (x) => x ** 2, args = [2], t = 100
Output:
[]
Explanation:
const cancelTimeMs = 50;
const cancel = cancellable(fn, args, t);
setTimeout(cancel, cancelTimeMs);
The cancellation was scheduled to occur after 50ms, and fn was never executed.
Example 3
Input:
fn = (x1, x2) => x1 * x2, args = [2,4], t = 30
Output:
[{"time":30,"returned":8}]
Explanation:
const cancelTimeMs = 100;
const cancel = cancellable(fn, args, t);
setTimeout(cancel, cancelTimeMs);
The cancellation was scheduled to occur after 100ms, and fn was executed at 30ms, so the cancellation had no effect.
Constraints
fn is a function
args is a valid JSON array
1 <= args.length <= 10
2 <= t <= 1000
1 <= cancelTimeMs <= 1000
setTimeout and clearTimeout
Intuition
Use JavaScript’s built-in setTimeout to schedule the function execution and clearTimeout to cancel it before it runs.
Steps
- Create a timeout using
setTimeoutthat callsfnwith spread arguments aftertmilliseconds - Store the timeout ID returned by
setTimeout - Return a cancel function that uses
clearTimeoutwith the stored ID to cancel the scheduled execution
import threading
def cancellable(fn, args, t):
timer = threading.Timer(t / 1000, lambda: fn(*args))
timer.start()
def cancel_fn():
timer.cancel()
return cancel_fnComplexity
- Time: O(1) for setup and cancellation
- Space: O(1) - only stores the timeout ID
- Notes: Most efficient approach using native browser APIs
Promise with Cancellation Flag
Intuition Wrap the timeout logic in a Promise and use a boolean flag to track whether cancellation has occurred before execution.
Steps
- Create a
cancelledflag initialized tofalse - Create a Promise that resolves after
tmilliseconds - Inside the Promise callback, check if not cancelled before executing
fn - Return a cancel function that sets the
cancelledflag totrue
import threading
def cancellable(fn, args, t):
cancelled = threading.Event()
def execute():
cancelled.wait(t / 1000)
if not cancelled.is_set():
fn(*args)
thread = threading.Thread(target=execute)
thread.start()
def cancel_fn():
cancelled.set()
return cancel_fnComplexity
- Time: O(1) for setup and cancellation
- Space: O(1) - only stores the cancellation flag
- Notes: Less efficient than clearTimeout as the timeout still completes internally
Async/Await with Cancellation
Intuition Use async/await syntax with a cancellation flag to handle the delayed execution in a more readable, modern JavaScript style.
Steps
- Create a
shouldCancelflag initialized tofalse - Define an async function that awaits a Promise-based delay of
tmilliseconds - After the delay, check if not cancelled before executing
fn - Start the async execution immediately
- Return a cancel function that sets the
shouldCancelflag totrue
import asyncio
def cancellable(fn, args, t):
cancelled = False
async def execute():
await asyncio.sleep(t / 1000)
nonlocal cancelled
if not cancelled:
fn(*args)
# Start the coroutine as a task
loop = asyncio.get_event_loop()
loop.create_task(execute())
def cancel_fn():
nonlocal cancelled
cancelled = True
return cancel_fnComplexity
- Time: O(1) for setup and cancellation
- Space: O(1) - only stores the cancellation flag
- Notes: More readable syntax but functionally similar to Promise approach