API · Async
mapAsync()
Run promise-returning work with bounded concurrency, optional completion-order output, retries, timeouts, and cancellation.
Signature
mapAsync<U>(
fn: (value: T, context: C) => U | PromiseLike<U>,
options?: MapAsyncOptions<T, C> | null,
): Exstream<Awaited<U>, C>
interface MapAsyncOptions<T, C extends object> {
concurrency?: number
ordered?: boolean
retry?: number | MapAsyncRetry<T, C> | null
timeout?: number | null
signal?: AbortSignal
}
interface MapAsyncRetry<T, C extends object> {
retries?: number
delay?: number | ((attempt: number, error: ExstreamError<T>, value: T, context: C) => number | PromiseLike<number>)
when?: (error: ExstreamError<T>, value: T, context: C, attempt: number) => boolean | PromiseLike<boolean>
} Example
const profiles = exstream(userIds).mapAsync(fetchProfile, {
concurrency: 8,
ordered: false,
retry: {
retries: 3,
delay: (attempt) => 250 * 2 ** (attempt - 1),
when: (error) => error.status === 429,
},
timeout: 10_000,
}) Parameters
fn-
Called for each successful input when a concurrency slot is available. It may return immediately or return a promise-like value. The resolved value is emitted. The same input and context are reused across retry attempts.
concurrency-
Maximum active tasks. Positive integers and
Infinityare accepted; zero, negative values, fractions, and non-numeric values are rejected. A retry and its delay retain the task's slot. Use a finite value at external I/O boundaries. ordered-
With
true, results are emitted in input order even when later tasks finish first. Withfalse, each result is emitted as soon as its task completes. Only literal booleans are accepted. retry-
A number is the count of additional attempts after the first failure. An object configures
retries,delay, andwhen. Zero andnulldisable retries. Fatal failures are never retried. retry.retries-
Number of additional attempts. For example,
retries: 2allows at most three callback calls for one input. retry.delay-
A fixed finite delay or a function returning one, synchronously or asynchronously. The function receives the one-based failed attempt, contextual error, input, and context. Invalid returned delays become record failures.
retry.when-
Return or resolve to
falseto stop retrying that failure. The attempt number is one-based. A thrown or rejected policy function becomes the final record failure. timeout-
Maximum milliseconds for each individual attempt, including
0. A timeout is eligible for retry and producesMapAsyncTimeoutErrorwith codeEXSTREAM_MAP_ASYNC_TIMEOUT, plustimeoutandattemptfields. signal-
Aborts this operator. A pre-aborted signal prevents work from starting. Pass
context.signalinto cancellable I/O so cancellation and per-attempt timeouts can stop the underlying operation rather than only ignore its late result.
Passing null or undefined for options applies every default. Other non-object values and arrays are rejected when the operator is created.
The JavaScript runtime normalizes numeric policy fields with Number(), so any coercible value is accepted when it produces the required integer or finite value. The TypeScript API deliberately exposes these fields as numbers; use actual numbers for portable, explicit configuration. ordered is strictly boolean.
Order and pressure
At most concurrency callbacks are active. Upstream demand pauses when all slots are occupied. In ordered mode, completed results may wait in memory behind a slower earlier input; unordered mode avoids that head-of-line delay and emits completion order.
The callback context exposes context.input, context.signal, and custom upstream fields. It is created lazily when fn declares its second parameter, retry.when declares its third, or a dynamic retry.delay declares its fourth. Declare those positional parameters rather than retrieving them through rest arguments when a materialized context is required. The context remains the same object across attempts and continues with the emitted result. During a timed attempt, only its signal is temporarily replaced with an attempt-specific signal and restored afterward.
Retries
Retry policy applies per input and per failed attempt. The callback receives the same value and record context each time. Delay and policy evaluation happen inside the same concurrency slot, so a large retry delay reduces available throughput.
Timeouts are also per attempt. Exstream aborts the attempt context signal when the deadline expires. JavaScript promises themselves are not cancellable, so the callback must forward that signal to fetch, database clients, or other cancellable APIs.
Errors
A thrown callback error, rejected promise, exhausted retry policy, invalid dynamic delay, or timeout becomes a contextual record error. If handled downstream, later tasks continue. Fatal graph failures and external cancellation abort the operator immediately and bypass retry policy.
Cancelling the branch stops new scheduling and ignores late completions from work that could not be cancelled.
Forms
mapAsync() is available on streams and reusable pipelines. The direct standalone form requires an explicit options argument; pass null for defaults:
stream.mapAsync(fn, options)
exstream.pipeline().mapAsync(fn, options)
exstream.mapAsync(fn, null, stream)
stream.through(exstream.mapAsync(fn, options))