shelving/util/asyncmodule

Helpers for working with promises and async values: detecting sync/async values, running concurrent promises safely, creating deferreds, and handling cancellation with AbortSignal.

  • awaitValues() differs from Promise.all in one important way: it waits for all promises to settle before rejecting, so no promise is left dangling in unhandled purgatory.
  • awaitRace() silently absorbs rejections from the losing arm(s), preventing unhandled-rejection warnings in cancellation/timeout patterns.

Usage

Detecting sync vs async values

ts
import { isAsync, notAsync, throwAsync, assertAsync, assertNotAsync } from "shelving/util";

isAsync(promise);      // true
notAsync("hello");     // true

// Suspense-style: throw the promise if async, return value if sync
const value = throwAsync(maybePromise);

Running concurrent promises

ts
import { awaitValues, awaitErrors } from "shelving/util";

// Like Promise.all() but waits for ALL to settle before rejecting
const [a, b] = await awaitValues(fetchUser(), fetchConfig());

// Collect rejection reasons without throwing
const errors = await awaitErrors(op1(), op2(), op3());

Creating a deferred

ts
import { createDeferred } from "shelving/util";

const { promise, resolve, reject } = createDeferred<string>();

// Later — from outside the promise executor
resolve("done");

Delays and cancellation

ts
import { getDelay, awaitAbort, awaitRace } from "shelving/util";

await getDelay(500); // wait 500 ms

// Combine a timeout with an AbortSignal — whichever fires first wins
await awaitRace(getDelay(3000), awaitAbort(signal));

Flushing microtasks (tests)

ts
import { runMicrotasks } from "shelving/util";

await runMicrotasks(); // drain all queued microtasks

Functions

Go

isAsync()function

Is a value an asynchronous value implementing a then() function.

isAsync(value: PromiseLike<T> | T): value is PromiseLike<T>
Go

notAsync()function

Is a value a synchronous value.

notAsync(value: PromiseLike<T> | T): value is T
Go

throwAsync()function

Throw the value if it's an async (promised) value.

throwAsync(value: PromiseLike<T> | T): T
Go

assertNotAsync()function

Assert an unknown value is synchronous (i.e. does not have a .then() method).

assertNotAsync(value: PromiseLike<T> | T): asserts value is T
Go

assertAsync()function

Assert an unknown value is asynchronous (i.e. has a .then() method).

assertAsync(value: PromiseLike<T> | T): asserts value is PromiseLike<T>
Go

assertPromise()function

Assert that an unknown value is a Promise.

assertPromise(value: Promise<T> | T): asserts value is Promise<T>
Go

runMicrotasks()function

Run any queued microtasks now.

runMicrotasks(): Promise<void>
Go

awaitValues()function

Get the result of multiple promises concurrently.

awaitValues(...promises: T): Promise<{ readonly [P in keyof T]: Awaited<T[P]> }>
Go

awaitErrors()function

Get the rejection reasons of multiple promises (concurrently).

awaitErrors(...promises: PromiseLike<unknown>[]): Promise<ImmutableArray<unknown>>
Go

createDeferred()function

Create a deferred to access the resolve() and reject() functions of a promise.

createDeferred(): Deferred<T>
Go

getDelay()function

Get a promise that automatically resolves after a delay.

getDelay(ms: number): Promise<void>
Go

awaitAbort()function

Get a promise that rejects with the signal's reason when an AbortSignal fires.

awaitAbort(signal: AbortSignal): Promise<never>
Go

awaitRace()function

Race promises like Promise.race() but silently swallow rejections from the losers.

awaitRace(...promises: Promise<T>[]): Promise<T>

Classes

Go

BasePromiseclass

Promise designed for extending with ._resolve() and ._reject() methods that can be accessed by subclasses.

new BasePromise<T>()

Types

Go

Deferredtype

Deferred allows you to access the internal resolve/reject callbacks of a Promise.

{
	promise: Promise<T>;
	resolve: ValueCallback<T>;
	reject: ErrorCallback;
}