Prom
Promise utilities for asynchronous operations.
Provides utilities for working with Promises including deferred promise creation, promise combinators, and async control flow patterns.
Import
import { Prom } from '@wollybeard/kit'
import * as Prom from '@wollybeard/kit/prom'
Deferred
[I]
Deferred
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:
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
// 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
<$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:
const deferred = createDeferred<number>()
setTimeout(() => {
deferred.resolve(42)
}, 1000)
const result = await deferred.promise // 42
// Strict mode prevents multiple resolutions
const deferred = createDeferred<number>({ strict: true })
deferred.resolve(1)
deferred.resolve(2) // Throws error
Type Guards
[F]
isShape
(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:
// 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
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
type AnyAny = Promise<any>
Type representing a Promise of any type. Less type-safe than Any, use with caution.
[U]
Maybe
type Maybe<$Type> = $Type | Promise<$Type>
Type representing a value that may or may not be wrapped in a Promise.
Examples:
// 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
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:
// 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
type Envelope<T = unknown> = {
fail: boolean
value: T
async: boolean
}
Envelope containing execution metadata.
Utilities
[F]
maybeAsync
<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 promisehandlers
- 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:
// 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
<$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)
fromPromise.reject(Error)
- Know whether execution was sync or async
- Handle errors without try/catch blocks
Examples:
// 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
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.