Обзор

All Settled

Run keyed behaviors and collect every child result.

parallel

Install

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

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

ts@lib/flow/all-settled.ts
import { type Result, ok } from "./core/result";
import {
type BehaviorRecord,
type RecordContext,
type RecordInput,
type SettledRecord,
abortChildren,
normalizedConcurrency,
runChild,
} from "./core/record";
import {
type Behavior,
type Cancelled,
type RuntimeContext,
type Thrown,
resultFromSignal,
thrown,
} from "./core/runtime";
/**
* Runs keyed behaviors and collects every child result.
*
* Unlike `all`, domain failures from one child do not cancel siblings. Parent cancellation still
* aborts active children and returns a cancellation runtime error for the whole operation.
*
* @typeParam B - Behavior record type.
* @param behaviors - Keyed behaviors to run with the same input.
* @param options - Optional concurrency limit.
* @returns A behavior producing a keyed record of child results.
*
* @example
* ```ts
* const loadEverything = allSettled({
* profile: loadProfile,
* recommendations: loadRecommendations,
* });
* ```
*/
export function allSettled<const B extends BehaviorRecord>(
behaviors: B,
options?: { concurrency?: number },
): Behavior<RecordInput<B>, SettledRecord<B>, never, RecordContext<B>> {
return async (input, context) => {
if (context.signal.aborted) {
return resultFromSignal(context.signal);
}
return runSettled(behaviors, input, context, options?.concurrency);
};
}
function runSettled<const B extends BehaviorRecord>(
behaviors: B,
input: RecordInput<B>,
context: RuntimeContext<RecordContext<B>>,
concurrency: number | undefined,
): Promise<Result<SettledRecord<B>, Cancelled | Thrown>> {
const keys = Object.keys(behaviors) as Array<keyof B>;
const output = {} as SettledRecord<B>;
const childControllers: AbortController[] = [];
if (keys.length === 0) {
return Promise.resolve(ok(output));
}
return new Promise((resolve) => {
let nextIndex = 0;
let active = 0;
let completed = 0;
let settled = false;
const limit = normalizedConcurrency(concurrency, keys.length);
const finish = (result: Result<SettledRecord<B>, Cancelled | Thrown>) => {
if (settled) {
return;
}
settled = true;
context.signal.removeEventListener("abort", onAbort);
abortChildren(childControllers, result.ok ? undefined : result.error);
resolve(result);
};
const onAbort = () => finish(resultFromSignal(context.signal));
context.signal.addEventListener("abort", onAbort, { once: true });
const startNext = () => {
if (settled) {
return;
}
while (active < limit && nextIndex < keys.length) {
const key = keys[nextIndex];
if (key === undefined) {
break;
}
const behavior = behaviors[key];
if (behavior === undefined) {
break;
}
nextIndex += 1;
active += 1;
runChild(
behavior as unknown as Behavior<RecordInput<B>, unknown, unknown, RecordContext<B>>,
input,
context,
childControllers,
)
.then((result) => {
active -= 1;
if (settled) {
return;
}
output[key] = result as SettledRecord<B>[typeof key];
completed += 1;
if (completed === keys.length) {
finish(ok(output));
return;
}
startNext();
})
.catch((error: unknown) => {
active -= 1;
if (settled) {
return;
}
output[key] = thrown(error) as SettledRecord<B>[typeof key];
completed += 1;
if (completed === keys.length) {
finish(ok(output));
return;
}
startNext();
});
}
};
startNext();
});
}

Update the import paths to match your project setup.

Run keyed Behavior values and collect every child result instead of failing fast.

Usage

allSettled is for fan-out work where partial success is useful. Each output key contains the child Result, so callers can inspect successes and failures independently.

Use it when:

  • Render partial data when some requests fail.
  • Collect validation results for several independent checks.
  • Avoid fail-fast Behavior while still respecting parent cancellation.
import { allSettled } from "@/lib/flow/all-settled";
import { task } from "@/lib/flow/task";
const loadPanels = allSettled({
profile: task(() => fetch("/api/profile").then((r) => r.json())),
alerts: task(() => fetch("/api/alerts").then((r) => r.json())),
});

Behavior

allSettled runs every child up to the concurrency limit and stores each child result under its original key.

Child domain and runtime errors are captured inside the settled output. Parent cancellation still stops the whole operation and returns Cancelled.

Types

Every key maps to a Result containing that child output, domain error, or runtime error.

Import from @/lib/flow/all-settled.

Composition Notes

Use allSettled for diagnostics, partial data, and batch-style workflows.

Do not use allSettled when the first failure should stop work quickly; use all.

Source

ts@lib/flow/all-settled.ts
import { type Result, ok } from "./core/result";
import {
type BehaviorRecord,
type RecordContext,
type RecordInput,
type SettledRecord,
abortChildren,
normalizedConcurrency,
runChild,
} from "./core/record";
import {
type Behavior,
type Cancelled,
type RuntimeContext,
type Thrown,
resultFromSignal,
thrown,
} from "./core/runtime";
/**
* Runs keyed behaviors and collects every child result.
*
* Unlike `all`, domain failures from one child do not cancel siblings. Parent cancellation still
* aborts active children and returns a cancellation runtime error for the whole operation.
*
* @typeParam B - Behavior record type.
* @param behaviors - Keyed behaviors to run with the same input.
* @param options - Optional concurrency limit.
* @returns A behavior producing a keyed record of child results.
*
* @example
* ```ts
* const loadEverything = allSettled({
* profile: loadProfile,
* recommendations: loadRecommendations,
* });
* ```
*/
export function allSettled<const B extends BehaviorRecord>(
behaviors: B,
options?: { concurrency?: number },
): Behavior<RecordInput<B>, SettledRecord<B>, never, RecordContext<B>> {
return async (input, context) => {
if (context.signal.aborted) {
return resultFromSignal(context.signal);
}
return runSettled(behaviors, input, context, options?.concurrency);
};
}
function runSettled<const B extends BehaviorRecord>(
behaviors: B,
input: RecordInput<B>,
context: RuntimeContext<RecordContext<B>>,
concurrency: number | undefined,
): Promise<Result<SettledRecord<B>, Cancelled | Thrown>> {
const keys = Object.keys(behaviors) as Array<keyof B>;
const output = {} as SettledRecord<B>;
const childControllers: AbortController[] = [];
if (keys.length === 0) {
return Promise.resolve(ok(output));
}
return new Promise((resolve) => {
let nextIndex = 0;
let active = 0;
let completed = 0;
let settled = false;
const limit = normalizedConcurrency(concurrency, keys.length);
const finish = (result: Result<SettledRecord<B>, Cancelled | Thrown>) => {
if (settled) {
return;
}
settled = true;
context.signal.removeEventListener("abort", onAbort);
abortChildren(childControllers, result.ok ? undefined : result.error);
resolve(result);
};
const onAbort = () => finish(resultFromSignal(context.signal));
context.signal.addEventListener("abort", onAbort, { once: true });
const startNext = () => {
if (settled) {
return;
}
while (active < limit && nextIndex < keys.length) {
const key = keys[nextIndex];
if (key === undefined) {
break;
}
const behavior = behaviors[key];
if (behavior === undefined) {
break;
}
nextIndex += 1;
active += 1;
runChild(
behavior as unknown as Behavior<RecordInput<B>, unknown, unknown, RecordContext<B>>,
input,
context,
childControllers,
)
.then((result) => {
active -= 1;
if (settled) {
return;
}
output[key] = result as SettledRecord<B>[typeof key];
completed += 1;
if (completed === keys.length) {
finish(ok(output));
return;
}
startNext();
})
.catch((error: unknown) => {
active -= 1;
if (settled) {
return;
}
output[key] = thrown(error) as SettledRecord<B>[typeof key];
completed += 1;
if (completed === keys.length) {
finish(ok(output));
return;
}
startNext();
});
}
};
startNext();
});
}