Reference
yieldless/task
Structured concurrency with a shared AbortSignal and sibling cancellation.
yieldless/task gives you a small structured-concurrency primitive for normal async functions.
Exports
type TaskFactory<T> = (signal: AbortSignal) => PromiseLike<T> | Tinterface TaskGroup { readonly signal: AbortSignal; spawn(task): Promise<T> }runTaskGroup(operation, options?): Promise<T>
What runTaskGroup() guarantees
- All spawned tasks share one AbortSignal
- The group can inherit cancellation from an upstream AbortSignal
- The first task failure aborts the group immediately
- The group waits for every child task to settle before returning
- The original failure is rethrown after cleanup
Example
import { runTaskGroup } from "yieldless/task";
const controller = new AbortController();
const repository = await runTaskGroup(async (group) => {
const refs = group.spawn((signal) => loadRefs(signal));
const branches = group.spawn((signal) => loadBranches(signal));
return {
refs: await refs,
branches: await branches,
};
}, {
signal: controller.signal,
});What it does not guarantee
Task cancellation is cooperative. Your spawned function must check or forward the signal for cancellation to take effect.
group.spawn((signal) => runCommand("git", ["fetch"], { signal }));Good fits
- Parallel repository reads that should rise and fall together
- Request-scoped fan-out in HTTP handlers
- Background jobs that launch multiple abortable I/O operations
Good
Use the group for work with one lifecycle.
const summary = await runTaskGroup(async (group) => {
const status = group.spawn((signal) => readStatus(repoPath, signal));
const branches = group.spawn((signal) => readBranches(repoPath, signal));
return {
status: await status,
branches: await branches,
};
}, {
signal: request.signal,
});Let failures throw inside runTaskGroup() when the body is promise-native. If you need tuple-native fan-out, use yieldless/all.
Avoid
Do not spawn children after the group body has returned.
let groupRef: TaskGroup | undefined;
await runTaskGroup((group) => {
groupRef = group;
return "done";
});
groupRef?.spawn(loadLater);Do not swallow child failures unless you intentionally convert them to successful values. The group uses thrown failures to abort siblings.