Skip to content

race

race takes multiple async tasks and resolves or rejects with the first one to settle, just like Promise.race. The remaining tasks are cancelled.

Best for first-to-finish Use race when you want the fastest result from multiple alternatives, or when you want to combine a task with a timeout.

Root package:

import { race } from "@vgerbot/async";

Module subpath:

import { race } from "@vgerbot/async/control-flow";

Leaf subpath:

import { race } from "@vgerbot/async/control-flow/race";
import { race } from "@vgerbot/async";
const handle = race({},
async (token) => {
await token.sleep(100);
return "fast";
},
async (token) => {
await token.sleep(500);
return "slow";
},
);
const result = await handle; // "fast"
  • Fastest response: Query multiple sources and use whichever responds first.
  • Timeout patterns: Race a task against a delay to implement manual timeouts.
  • Redundant operations: Run the same operation against multiple endpoints.
  • Cancellable alternatives: Cancel losing tasks automatically.
function race(
options: CancellableOptions,
...args: AsyncTask<unknown>[]
): CancellableHandle<unknown>;
Parameter Type Default Description
options CancellableOptions Cancellable configuration options.
...args AsyncTask<T>[] Rest parameters of async tasks to race. Each receives a CancellableToken.

Returns a CancellableHandle<T> that resolves or rejects with the first task to settle.

const handle = race({}, task1, task2);
const result = await handle;
handle.cancel("No longer needed");
handle.isCancelled();
handle.signal;

race wraps Promise.race with cancellation support. All tasks start concurrently and share a CancellableToken. When the first task settles, the handle adopts its result. The other tasks receive a cancellation signal.

const handle = race({},
async (token) => {
await token.sleep(50);
return "quick";
},
async (token) => {
await token.sleep(5000);
return "very slow";
},
);
// Resolves with "quick" after 50ms
// The second task is cancelled

If the first task to settle rejects, the handle rejects with that error. Tasks that settle later are ignored.

try {
await race({},
async () => { throw new Error("immediate failure"); },
async (token) => { await token.sleep(100); return "late success"; },
);
} catch (error) {
console.log("Race failed:", error);
}

Calling cancel() on the handle cancels all participating tasks.

const handle = race({ name: "raceOperation" },
async (token) => { await token.sleep(5000); return "result"; },
);
setTimeout(() => handle.cancel("User cancelled"), 100);

race is generic over T. All tasks must return the same type.

const handle = race({},
async () => 1,
async () => 2,
);
const result = await handle; // number
  • any resolves with the first task to fulfill (ignores rejections).
  • allSettled waits for all tasks to settle.
  • parallel runs all tasks and collects all results.
  • timeout wraps a single task with a deadline.