class Task

A single async task with error-aware utilities like retries and timeouts.

Constructors

Type Parameters

Static Methods

acquireRelease<
R,
T,
E,
>
(unnamed 0: { acquire: (signal?: AbortSignal) => Promise<R>; release: (resource: R) => Promise<void>; use: (resource: R) => Task<T, E>; }): Task<T, E>

Acquires a resource, runs a task that uses it, and releases it regardless of success or failure. Useful for resource lifecycle management when the use computation is a composed Task chain.

The acquire function receives an optional AbortSignal that is active when the task is run with a signal. The release function always runs and does not receive a signal — cleanup should not be cancellable.

all<
T,
E,
>
(tasks: Task<T, E>[]): Task<T[], E>

Runs multiple tasks and collects results. Rejects on the first failure.

allSettled<
T,
E,
>
(tasks: Task<T, E>[]): Task<Result<T, E>[], never>

Runs multiple tasks and collects all results without throwing.

chain(arr: []): Task<void, never>

Runs tasks sequentially, passing the successful value from one to the next. If any task fails, the chain is short-circuited and the error is returned.

Return types -> parameter type inference is supported up to an arbitrary 7 tasks. If you need more, you can just chain two chains.

Example usage:

Task.chain([
  () => Task.of(() => 1),
  (prev) => Task.of(() => prev + 1),
  (prev) => Task.of(() => `Result: ${prev}`),
]).tap((result) => {
  console.log(result) // "Result: 2"
}).run()
chain<
R,
E1,
>
(tasks: [() => Task<R, E1>]): Task<R, E1>
chain<
A,
R,
E1,
E2,
>
(tasks: [() => Task<A, E1>, (prev: A) => Task<R, E2>]): Task<R, E1 | E2>
chain<
A,
B,
R,
E1,
E2,
E3,
>
(tasks: [
() => Task<A, E1>,
(prev: A) => Task<B, E2>,
(prev: B) => Task<R, E3>,
]
): Task<R,
E1
| E2
| E3
>
chain<
A,
B,
C,
R,
E1,
E2,
E3,
E4,
>
(tasks: [
() => Task<A, E1>,
(prev: A) => Task<B, E2>,
(prev: B) => Task<C, E3>,
(prev: C) => Task<R, E4>,
]
): Task<R,
E1
| E2
| E3
| E4
>
chain<
A,
B,
C,
D,
R,
E1,
E2,
E3,
E4,
E5,
>
(tasks: [
() => Task<A, E1>,
(prev: A) => Task<B, E2>,
(prev: B) => Task<C, E3>,
(prev: C) => Task<D, E4>,
(prev: D) => Task<R, E5>,
]
): Task<R,
E1
| E2
| E3
| E4
| E5
>
chain<
A,
B,
C,
D,
E,
R,
E1,
E2,
E3,
E4,
E5,
E6,
>
(tasks: [
() => Task<A, E1>,
(prev: A) => Task<B, E2>,
(prev: B) => Task<C, E3>,
(prev: C) => Task<D, E4>,
(prev: D) => Task<E, E5>,
(prev: E) => Task<R, E6>,
]
): Task<R,
E1
| E2
| E3
| E4
| E5
| E6
>
chain<
A,
B,
C,
D,
E,
F,
R,
E1,
E2,
E3,
E4,
E5,
E6,
E7,
>
(tasks: [
() => Task<A, E1>,
(prev: A) => Task<B, E2>,
(prev: B) => Task<C, E3>,
(prev: C) => Task<D, E4>,
(prev: D) => Task<E, E5>,
(prev: E) => Task<F, E6>,
(prev: F) => Task<R, E7>,
]
): Task<R,
E1
| E2
| E3
| E4
| E5
| E6
| E7
>
private
mergeExternalSignals(
outer: AbortSignal,
inner: AbortSignal,
): { signal: AbortSignal; cleanup: () => void; }
of<
R,
E,
>
(task: (signal?: AbortSignal) => Promisable<R>): Task<R, E>

Creates a Task from a sync or async function. The function receives an optional AbortSignal that is active when the task is run with a signal via withSignal or run.

Note: the error type E is unchecked and represents the expected error shape rather than a runtime guarantee.

race<
T,
E,
>
(tasks: Task<T, E>[]): Task<Result<T, E[]>, never>

Runs tasks concurrently and resolves with the first settled result.

Note: If all tasks fail, the errors array is in completion order (the order tasks finished), not task order. This is nondeterministic due to concurrent execution.

Properties

private
runTask: (signal?: AbortSignal) => Promise<T>

Methods

private
delayWithSignal(
ms: number,
signal?: AbortSignal,
): Promise<void>
flatMap<U>(fn: (value: T) => Task<U, E>): Task<U, E>

Chains another task based on the successful value.

flatMapErr<F>(fn: (error: E) => Task<T, F>): Task<T, F>

Chains another task based on the error value.

map<
U,
E2 = E,
>
(fn: (value: T) => Promisable<U>): Task<U, E | E2>

Maps the successful value. Errors are passed through unchanged.

mapErr<F>(fn: (error: E) => Promisable<F>): Task<T, F>

Maps the error value. Successful values are passed through unchanged.

private
mergeSignals(
outer: AbortSignal,
inner?: AbortSignal,
): { signal: AbortSignal; cleanup: () => void; }
recover<U>(fn: (error: E) => Promisable<U>): Task<T | U, never>

Recovers from errors by mapping them to a successful value.

recoverWhen<
E2 extends E,
U,
>
(
guard: (error: E) => error is E2,
fn: (error: E2) => Promisable<U>,
): Task<T | U, Exclude<E, E2>>

Recovers from specific error types by mapping them to a successful value.

result(): Promise<Result<T, E>>

Executes the task and returns a tagged result instead of throwing.

retry(options: { attempts: number; delay?: number | ((
attempt: number,
error: E,
) => number)
; when?: (error: E) => boolean; }
): Task<T, E>

Retries the task when the predicate returns true.

run(): Promise<T>

Executes the task and resolves the value or throws the error.

protected
runWithSignal(signal?: AbortSignal): Promise<T>
tap(fn: (value: T) => Promisable<void>): Task<T, E>

Runs a side effect on the successful value.

tapErr(fn: (error: E) => Promisable<void>): Task<T, E>

Runs a side effect on the error value.

throwOn<E2 extends E>(guard: (error: E) => error is E2): Task<T, Exclude<E, E2>>

Throws the specified error types if encountered, propagating them to the caller.

timeout(
ms: number,
error?: E,
): Task<T, E>

Fails if the task does not complete within the specified time.

tryFlatMap<
U,
F,
>
(
fn: (value: T) => Task<U, F>,
errFn: (error: unknown) => Promisable<U>,
): Task<U, E>

Chains another task based on the successful value, with error handling for the mapper. If the mapper fails, errFn can recover by returning a success value.

tryMap<
U,
F = never,
>
(
fn: (value: T) => Promisable<U>,
errFn: (
error: unknown,
value: T,
) => Promisable<F>
,
): Task<U, E | F>

Maps successful values with fn and transforms errors with errFn. Both receive the original value so you can contextualize the mapping.

Wraps the task with an external abort signal.