AniUI Academy

Model cancellation like AbortController

hardAsync & Promises+350 XPPractice

When a modal closes or a route changes, the work it started is no longer wanted. AbortController is how the platform expresses that: a controller holds the abort() method, and the passive signal is handed to whatever should stop. Building it yourself makes the contract concrete.

Implement two things. First, createCanceller() returns an object { signal, cancel }. signal.cancelled starts as false and signal.reason starts as null. Calling cancel(reason) sets cancelled to true and stores reason, defaulting to the string "Cancelled", and synchronously calls every listener registered with signal.onCancel(listener), passing the reason. Cancelling twice does nothing the second time: the first reason is kept and listeners are not called again. A listener registered after cancellation is called immediately with the stored reason. Second, delay(ms, signal) returns a promise that resolves with undefined after ms milliseconds, but rejects with new Error(signal.reason) as soon as the signal is cancelled, clearing its timer. If the signal is already cancelled when delay is called, it rejects straight away. signal may be omitted, in which case the delay simply resolves.

Arguments

msnumber
delay's wait time in milliseconds.
signal{ cancelled: boolean, reason: any, onCancel: Function } | undefined
Optional signal from createCanceller(). Rejects the delay when cancelled.

Returns

Promise<void> — delay resolves after ms or rejects with Error(signal.reason) if the signal is cancelled. createCanceller() returns { signal, cancel } synchronously (not a promise).

Examples

const { signal, cancel } = createCanceller();
const message = delay(500, signal).then(() => "waited", (e) => e.message);
setTimeout(() => cancel("user left"), 10);
// await message === "user left" — rejected early, timer cleared

A cancel aborts a pending delay almost immediately.

const { signal, cancel } = createCanceller();
cancel(); // defaults the reason to "Cancelled"
let received = "not called";
signal.onCancel((reason) => { received = reason; });
// received === "Cancelled" — late listener fires immediately

Listeners registered after cancellation are invoked right away.

What it has to do

  • Start with cancelled false and reason null.
  • Default the cancel reason to the string Cancelled.
  • Call onCancel listeners synchronously, once, in registration order.
  • Call a listener registered after cancellation immediately.
  • Have delay reject with an Error carrying the reason, both when cancelled while waiting and when already cancelled.

Notes

  • Call onCancel listeners synchronously inside cancel so a delay listener can clear its timer before the current tick ends.
  • A listener registered after cancellation must fire immediately with the stored reason — otherwise late subscribers silently miss the event.

Stuck? The javascript course covers everything this challenge needs.

Try it yourself
Loading playground...
5 tests · +350 XP

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.