series
series executes async tasks one after another. Each task receives the result of the previous task (if any) and a CancellableToken. The final result is the output of the last task.
Best for ordered pipelines Use
serieswhen each step must complete before the next begins, and later steps may use earlier results.
Import
Section titled “Import”Root package:
import { series } from "@vgerbot/async";Module subpath:
import { series } from "@vgerbot/async/control-flow";Leaf subpath:
import { series } from "@vgerbot/async/control-flow/series";Quick example
Section titled “Quick example”import { series } from "@vgerbot/async";
const handle = series({}, async (token) => { await token.sleep(50); return 10; }, async (result, token) => { // result is 10 (output of previous task) return result * 2; }, async (result, token) => { // result is 20 return `Final: ${result}`; },);
const value = await handle; // "Final: 20"When to use series
Section titled “When to use series”- Sequential pipelines: Each step depends on the previous step’s output.
- Ordered side effects: Ensure operations happen in a specific order (e.g., create, update, notify).
- Resource-constrained environments: Avoid concurrent operations when resources are limited.
- Cancellable chains: Cancel the entire chain from outside.
// Single taskfunction series<T1>( options: CancellableOptions, task1: SeriesTask<void, T1>,): CancellableHandle<T1>;
// Two tasksfunction series<T1, T2>( options: CancellableOptions, task1: SeriesTask<void, T1>, task2: SeriesTask<T1, T2>,): CancellableHandle<T2>;
// Three tasksfunction series<T1, T2, T3>( options: CancellableOptions, task1: SeriesTask<void, T1>, task2: SeriesTask<T1, T2>, task3: SeriesTask<T2, T3>,): CancellableHandle<T3>;
// Four tasksfunction series<T1, T2, T3, T4>( options: CancellableOptions, task1: SeriesTask<void, T1>, task2: SeriesTask<T1, T2>, task3: SeriesTask<T2, T3>, task4: SeriesTask<T3, T4>,): CancellableHandle<T4>;
// Five or more (untyped chain)function series( options: CancellableOptions, task1: SeriesTask<void, unknown>, ...args: SeriesTask<unknown, unknown>[],): CancellableHandle<unknown>;The first task is a SeriesTask<void, T1> (receives only a token). Subsequent tasks receive the previous result and a token.
Parameters
Section titled “Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
options |
CancellableOptions |
— | Cancellable configuration options. |
task1 |
SeriesTask<void, T1> |
— | The first task to execute (receives only a token). |
...args |
SeriesTask<unknown, unknown>[] |
— | Additional tasks to execute in sequence. Each receives the previous task’s result. |
Return value
Section titled “Return value”Returns a CancellableHandle<T> that resolves to the result of the last task.
const handle = series({}, task1, task2, task3);
const result = await handle;handle.cancel("No longer needed");handle.isCancelled();handle.signal;Execution model
Section titled “Execution model”Tasks are executed one at a time. The first task receives only a CancellableToken. Each subsequent task receives the resolved value of the previous task and the same token. If any task rejects, the chain stops and the handle rejects with that error.
const handle = series({}, async (token) => { const res = await token.wrap(fetch("/api/step1")); return res.json(); }, async (data, token) => { const res = await token.wrap( fetch("/api/step2", { method: "POST", body: JSON.stringify(data) }), ); return res.json(); }, async (result, token) => { console.log("Final result:", result); return result; },);Tasks can also be CancellableHandle instances or Promise instances, which are awaited directly.
Error handling
Section titled “Error handling”If any task in the series rejects, the handle rejects immediately with that error. Remaining tasks are not executed.
try { await series({}, async () => "step1", async () => { throw new Error("step2 failed"); }, async () => "step3 (never runs)", );} catch (error) { console.log("Series failed:", error);}Cancellation
Section titled “Cancellation”All tasks share a single CancellableToken. Calling cancel() on the handle signals cancellation to the currently running task and prevents subsequent tasks from starting.
const handle = series({ name: "pipeline" }, async (token) => { await token.sleep(5000); return "slow"; }, async (result) => `${result} done`,);
setTimeout(() => handle.cancel("User cancelled"), 100);
try { await handle;} catch (error) { if (error instanceof CancelError) { console.log("Series was cancelled"); }}TypeScript tips
Section titled “TypeScript tips”For up to four tasks, series provides typed overloads so the result of each task is passed with the correct type to the next.
const handle = series({}, async (token) => 10, // returns number async (n: number, token) => `Value: ${n}`, // receives number, returns string async (s: string, token) => ({ result: s }), // receives string, returns object);
const result = await handle; // { result: string }For five or more tasks, the chain falls back to unknown types. Use explicit type annotations in this case.
Related APIs
Section titled “Related APIs”waterfallis similar but always passes results forward.parallelruns tasks concurrently.autoruns tasks with dependency-based scheduling.cancellablecreates a cancellable handle for custom tasks.