Overview

Wait

AbortSignal-aware delay that returns a typed cancellation Result.

asynccancellation

Install

$
Terminal window
pnpm dlx shadcn@latest add https://archyn.net/r/wait.json

Copy the files below into the target paths shown in each tab.

ts@lib/flow/wait.ts
import { type Result, ok } from "./core/result";
import { type Cancelled, resultFromSignal } from "./core/runtime";
/**
* Waits for a duration while observing an `AbortSignal`.
*
* Non-positive durations resolve immediately with success. If the signal aborts before the timer
* completes, the promise resolves with a cancellation result instead of throwing.
*
* @param ms - Duration in milliseconds.
* @param signal - Signal used to cancel the wait.
* @returns A promise resolving to success or cancellation.
*
* @example
* ```ts
* const waited = await wait(250, signal);
* ```
*/
export function wait(ms: number, signal: AbortSignal): Promise<Result<void, Cancelled>> {
if (signal.aborted) {
return Promise.resolve(resultFromSignal(signal));
}
if (ms <= 0) {
return Promise.resolve(ok(undefined));
}
return new Promise((resolve) => {
const timeout = setTimeout(() => {
signal.removeEventListener("abort", onAbort);
resolve(ok(undefined));
}, ms);
const onAbort = () => {
clearTimeout(timeout);
signal.removeEventListener("abort", onAbort);
resolve(resultFromSignal(signal));
};
signal.addEventListener("abort", onAbort, { once: true });
});
}

Update the import paths to match your project setup.

Wait for a duration while respecting AbortSignal cancellation.

Usage

wait is the low-level timing primitive used by retry, timeout-adjacent flows, and any workflow that needs a cancellable delay. It returns a typed Result rather than throwing on abort.

Use it when:

  • Pause between attempts in retry logic.
  • Add cancellation-aware sleeps inside custom Behavior values.
  • Avoid untracked setTimeout calls that keep running after a parent flow aborts.
import { wait } from "@/lib/flow/wait";
const controller = new AbortController();
const result = await wait(250, controller.signal);
if (!result.ok && result.error.kind === "cancelled") {
console.log("wait was cancelled");
}

Behavior

If the signal is already aborted, wait resolves immediately with Cancelled. Otherwise it starts a timer and removes its abort listener when the timer settles or cancellation occurs.

wait has no domain failure type. Its only failure is Cancelled, which preserves the signal reason when one is provided.

Types

The result is Promise<Result<void, Cancelled>>, so cancellation stays explicit and typed.

Import from @/lib/flow/wait.

Composition Notes

Use wait inside custom helpers or tasks that need AbortSignal-aware sleeping.

Do not use wait for domain retry policy by itself; use retry when the delay is tied to a failed attempt.

Source

ts@lib/flow/wait.ts
import { type Result, ok } from "./core/result";
import { type Cancelled, resultFromSignal } from "./core/runtime";
/**
* Waits for a duration while observing an `AbortSignal`.
*
* Non-positive durations resolve immediately with success. If the signal aborts before the timer
* completes, the promise resolves with a cancellation result instead of throwing.
*
* @param ms - Duration in milliseconds.
* @param signal - Signal used to cancel the wait.
* @returns A promise resolving to success or cancellation.
*
* @example
* ```ts
* const waited = await wait(250, signal);
* ```
*/
export function wait(ms: number, signal: AbortSignal): Promise<Result<void, Cancelled>> {
if (signal.aborted) {
return Promise.resolve(resultFromSignal(signal));
}
if (ms <= 0) {
return Promise.resolve(ok(undefined));
}
return new Promise((resolve) => {
const timeout = setTimeout(() => {
signal.removeEventListener("abort", onAbort);
resolve(ok(undefined));
}, ms);
const onAbort = () => {
clearTimeout(timeout);
signal.removeEventListener("abort", onAbort);
resolve(resultFromSignal(signal));
};
signal.addEventListener("abort", onAbort, { once: true });
});
}