Skip to content

Prom

Promise utilities for asynchronous operations.

Provides utilities for working with Promises including deferred promise creation, promise combinators, and async control flow patterns.

Import

typescript
import { Prom } from '@wollybeard/kit'
typescript
import * as Prom from '@wollybeard/kit/prom'

Deferred

[I] Deferred

typescript
interface Deferred<$Value> {
  /**
   * The promise that will be resolved or rejected.
   */
  promise: Promise<$Value>
  /**
   * Resolve the promise with a value.
   */
  resolve: (value: $Value) => void
  /**
   * Reject the promise with an error.
   */
  reject: (error: unknown) => void
  /**
   * Whether the promise has been resolved.
   */
  readonly isResolved: boolean
  /**
   * Whether the promise has been rejected.
   */
  readonly isRejected: boolean
  /**
   * Whether the promise has been settled (resolved or rejected).
   */
  readonly isSettled: boolean
}

A deferred promise with exposed resolve and reject functions.

Examples:

typescript
const 
deferred
= createDeferred<number>()
// Later resolve it
deferred
.resolve(42)
// Or reject it
deferred
.reject(new
Error
('failed'))
// Use the promise await
deferred
.promise // 42
typescript
// Check resolution state
const 
deferred
= createDeferred<number>()
console
.
log
(
deferred
.isResolved) // false
deferred
.resolve(42)
console
.
log
(
deferred
.isResolved) // true
console
.
log
(
deferred
.isSettled) // true

[F] createDeferred

typescript
<$T>(options ?: { strict?: boolean; } | undefined): Deferred<$T>

Parameters:

  • options - Configuration options

Returns: A deferred promise object

Create a deferred promise with exposed resolve and reject functions.

Examples:

typescript
const 
deferred
= createDeferred<number>()
setTimeout
(() => {
deferred
.resolve(42)
}, 1000) const
result
= await
deferred
.promise // 42
typescript
// Strict mode prevents multiple resolutions
const 
deferred
= createDeferred<number>({
strict
: true })
deferred
.resolve(1)
deferred
.resolve(2) // Throws error

Type Guards

[F] isShape

typescript
(value: unknown): boolean

Parameters:

  • value - The value to test.

Returns: True if the value has Promise-like shape.

Check if a value has the shape of a Promise. Tests for the presence of then, catch, and finally methods.

Examples:

typescript
// with a promise
Prom
.isShape(
Promise
.
resolve
(42)) // true
// with a thenable object
Prom
.isShape({
then
: () => {},
catch
: () => {},
finally
: () => {} }) // true
// with non-promise values
Prom
.isShape(42) // false
Prom
.isShape({}) // false

Types

[T] Any

typescript
type Any = Promise<unknown>

Type representing a Promise of unknown type. Useful for generic promise handling where the resolved type is not important.

[T] AnyAny

typescript
type AnyAny = Promise<any>

Type representing a Promise of any type. Less type-safe than Any, use with caution.

[U] Maybe

typescript
type Maybe<$Type> = $Type | Promise<$Type>

Type representing a value that may or may not be wrapped in a Promise.

Examples:

typescript
// function that accepts sync or async values
function 
process
<
T
>(
value
:
Maybe
<
T
>):
Promise
<
T
> {
return
Promise
.
resolve
(
value
)
}
process
(42) // accepts number
process
(
Promise
.
resolve
(42)) // accepts Promise<number>

[T] AwaitedUnion

typescript
type AwaitedUnion<$MaybePromise, $Additional> = $MaybePromise extends
  Promise<infer __promised__> ? Promise<Awaited<__promised__ | $Additional>>
  : $MaybePromise | $Additional

Type that adds an additional type to a potentially promised union. If the input is a Promise, the additional type is added to the promised value. If the input is not a Promise, creates a union with the additional type.

Examples:

typescript
// with promise input
type 
Result1
=
AwaitedUnion
<
Promise
<string>, number> // Promise<string | number>
// with non-promise input type
Result2
=
AwaitedUnion
<string, number> // string | number

[T] Envelope

typescript
type Envelope<T = unknown> = {
  fail: boolean
  value: T
  async: boolean
}

Envelope containing execution metadata.

Utilities

[F] maybeAsync

typescript
<T, R = T, E = unknown > (fn: () => T, handlers ?: MaybeAsyncHandlers<T extends Promise<infer U> ? U : T, R, E> = {}): T extends Promise<infer U> ? Promise<R | E | U> : T | R | E

Parameters:

  • fn - Function to execute that might return a promise
  • handlers - Object with then/catch handlers

Returns: The result, potentially wrapped in a Promise

Handle a function that might return a promise or a regular value, with unified handlers for both sync and async cases.

Implemented using maybeAsyncEnvelope internally.

Examples:

typescript
// Basic usage
const 
result
=
Prom
.maybeAsync(
() => fetchData(), {
then
: (
data
) => processData(
data
),
catch
: (
error
) => ({
success
: false,
error
}),
}, ) // Just error handling const
safeResult
=
Prom
.maybeAsync(
() => riskyOperation(), {
catch
: (
error
,
isAsync
) => {
console
.
error
(`Failed ${
isAsync
? 'async' : 'sync'}:`,
error
)
return null }, }, ) // Just success handling const
transformed
=
Prom
.maybeAsync(
() => getValue(), {
then
: (
value
) =>
value
.toUpperCase(),
}, )

[F] maybeAsyncEnvelope

typescript
<$return>(fn: () => $return): $return extends Promise<infer __awaited__> ? Promise<Envelope<__awaited__>> : Envelope<$return>

Parameters:

  • fn - Function to execute

Returns: Envelope (sync) or Promise of envelope (async) with execution metadata

Execute a function and return an envelope with metadata about the execution.

Returns metadata indicating:

  • channel: Whether the function succeeded ('succeed') or failed ('fail')
  • async: Whether execution was asynchronous (promise) or synchronous
  • value/error: The result value or thrown/rejected error

Never throws or rejects

  • all errors are captured in the envelope. Preserves sync/async distinction in both return type and metadata.

Useful when you need to:

  • Distinguish Promise.resolve(Error) from Promise.reject(Error)
  • Know whether execution was sync or async
  • Handle errors without try/catch blocks

Examples:

typescript
// Sync success
const 
result
=
Prom
.maybeAsyncEnvelope(() => 42)
// { channel: 'succeed', value: 42, async: false } // Sync failure const
result
=
Prom
.maybeAsyncEnvelope(() => {
throw new
Error
('fail')
}) // { channel: 'fail', error: Error('fail'), async: false } // Async success const
result
= await
Prom
.maybeAsyncEnvelope(() =>
Promise
.
resolve
('ok'))
// { channel: 'succeed', value: 'ok', async: true } // Async failure const
result
= await
Prom
.maybeAsyncEnvelope(() =>
Promise
.
reject
('error'))
// { channel: 'fail', error: 'error', async: true } // Promise resolving to Error (not a rejection!) const
result
= await
Prom
.maybeAsyncEnvelope(() =>
Promise
.
resolve
(new
Error
('value'))
) // { channel: 'succeed', value: Error('value'), async: true }

[I] MaybeAsyncHandlers

typescript
interface MaybeAsyncHandlers<T, R = T, E = unknown> {
  /**
   * Handler for successful values (sync or async).
   */
  then?: (value: T) => R

  /**
   * Handler for errors (sync or async).
   * @param error - The caught error
   * @param isAsync - Whether the error occurred asynchronously
   */
  catch?: (error: unknown, isAsync: boolean) => E
}

Options for handling values that might be promises.