Write createTimeouts(). It returns an object that makes timeouts and keeps track of them, so a widget or a test can cancel every timeout it started in one call, for example when it's removed from the page.
Its setTimeout(fn, delay, ...args) works like the global one: it calls fn(...args) once, delay ms later, and returns an ID.
Its clearTimeout(id) cancels the timeout with that ID, so fn never runs for it.
clearAll() cancels every timeout made through this object that hasn't run yet: their timers are cleared, so none is left waiting. Timeouts made elsewhere, with the global setTimeout or with another createTimeouts() object, keep running. The object still works afterwards: timeouts you make later run as usual.
pending() returns how many of its timeouts are still waiting: made, but not yet run or cancelled.
const timeouts = createTimeouts();
timeouts.setTimeout((name) => console.log("hi", name), 100, "Ada");
timeouts.setTimeout(() => console.log("saved"), 1000);
const id = timeouts.setTimeout(() => console.log("closed"), 2000);
timeouts.pending(); // 3
timeouts.clearTimeout(id);
timeouts.pending(); // 2
// at 100 ms: logs "hi Ada"
timeouts.pending(); // 1
timeouts.clearAll();
timeouts.pending(); // 0, and "saved" is never logged
const clock = useFakeTimers();
const timeouts = createTimeouts();
const fn = spy();
timeouts.setTimeout(fn, 100, "a", 1);
clock.tick(99);
expect(fn).toHaveBeenCalledTimes(0);
clock.tick(1);
expect(fn).toHaveBeenCalledTimes(1);
expect(fn).toHaveBeenLastCalledWith("a", 1);
const clock = useFakeTimers();
const timeouts = createTimeouts();
const a = spy();
const b = spy();
const id = timeouts.setTimeout(a, 100);
timeouts.setTimeout(b, 100);
timeouts.clearTimeout(id);
clock.tick(1000);
expect(a).not.toHaveBeenCalled();
expect(b).toHaveBeenCalledTimes(1);
const clock = useFakeTimers();
const timeouts = createTimeouts();
const fns = [spy(), spy(), spy()];
timeouts.setTimeout(fns[0], 0);
timeouts.setTimeout(fns[1], 100);
timeouts.setTimeout(fns[2], 200);
timeouts.clearAll();
expect(clock.pending()).toBe(0);
clock.tick(1000);
expect(fns.map((fn) => fn.callCount)).toEqual([0, 0, 0]);
const clock = useFakeTimers();
const mine = createTimeouts();
const other = createTimeouts();
const global = spy();
const theirs = spy();
const ours = spy();
setTimeout(global, 100);
other.setTimeout(theirs, 100);
mine.setTimeout(ours, 100);
mine.clearAll();
clock.tick(100);
expect(ours).not.toHaveBeenCalled();
expect(theirs).toHaveBeenCalledTimes(1);
expect(global).toHaveBeenCalledTimes(1);
const clock = useFakeTimers();
const timeouts = createTimeouts();
expect(timeouts.pending()).toBe(0);
const id = timeouts.setTimeout(() => {}, 100);
timeouts.setTimeout(() => {}, 200);
timeouts.setTimeout(() => {}, 300);
expect(timeouts.pending()).toBe(3);
timeouts.clearTimeout(id);
expect(timeouts.pending()).toBe(2);
clock.tick(200);
expect(timeouts.pending()).toBe(1);
timeouts.clearAll();
expect(timeouts.pending()).toBe(0);
const clock = useFakeTimers();
const timeouts = createTimeouts();
timeouts.setTimeout(() => {}, 100);
timeouts.clearAll();
const fn = spy();
timeouts.setTimeout(fn, 100);
expect(timeouts.pending()).toBe(1);
clock.tick(100);
expect(fn).toHaveBeenCalledTimes(1);
expect(timeouts.pending()).toBe(0);
Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.
Ctrl + Enter runs the tests (⌘ + Enter on a Mac).
I'm here to help with Clear all timeouts. I give hints, never the solution, so you get to solve it yourself. Ask for a hint, select code to ask about it, or press Ask Tuto why on a failing test.
Your first 3 answers need no account. for free to keep asking.