Easy

Cancellable Interval

Prompt

Create a function called setCancellableInterval that works like JavaScript's built-in setInterval, but returns a cancel function instead of a numeric timer ID.

Your function should accept a callback function and a time interval in milliseconds. It should repeatedly execute the callback at the specified interval. Most importantly, it should return a function that, when called, stops the repeating execution.

Playground

Hint 1

You'll need to call setInterval inside your function and store the timer ID it returns. That ID is your key to stopping the interval later.

Hint 2

Return an arrow function (or regular function) that calls clearInterval with the stored timer ID. This uses closure to "remember" the ID even after setCancellableInterval has finished executing.

Hint 3

The built-in setInterval already supports passing extra arguments after the delay. You can forward them with rest parameters: setCancellableInterval(func, delay, ...args) and then setInterval(func, delay, ...args).

Solution

Explanation

This is a classic closure question, and the good news is the solution is only a few lines long. The tricky part isn't the code itself; it's understanding why the pattern works.

When you call setInterval, it returns a numeric timer ID. That ID is the only handle you have to stop the interval later with clearInterval. The problem with the native API is that you have to keep track of that ID yourself, passing it around wherever you might need to cancel. This function wraps that bookkeeping into a cleaner interface.

function setCancellableInterval(func, delay, ...args) {
const timerId = setInterval(func, delay, ...args);

return () => {
clearInterval(timerId);
};
}

The timerId variable lives inside setCancellableInterval's scope. When we return the arrow function, that inner function forms a closure over timerId. Even after setCancellableInterval has finished running and its execution context would normally be garbage collected, the returned function keeps a reference to timerId alive. That's the whole mechanism.

The ...args rest parameter is a nice touch that candidates sometimes miss. The native setInterval supports passing extra arguments after the delay, and those get forwarded to the callback on each tick. By using rest/spread, our wrapper preserves that capability:

const cancel = setCancellableInterval(
(greeting) => console.log(greeting),
1000,
'Hello!'
);

You can also write an even shorter version by spreading all arguments directly:

function setCancellableInterval(...args) {
const timerId = setInterval(...args);
return () => clearInterval(timerId);
}

This works perfectly fine, though the explicit parameter version is easier to read and self-documents what the function expects. In an interview, either version would be accepted. The interviewer is really looking for whether you understand closures and can articulate why the returned function still has access to timerId.