like .then, but for synchronous values and thenables.
when is a small, but useful package for chaining a callback
onto an awaitable (a value or a thenable).
For thenable values, .then is used to invoke the callback after resolution.
Otherwise, the callback is called immediately.
This makes it easy to write one code path that supports both synchronous and asynchronous values.
when is especially useful in libraries supporting awaitable APIs or libraries that accept user-provided hooks,
loaders, or resolvers that may or may not return promises.
when preserves synchronous values when possible, thus avoiding microtask scheduling and needless promise-allocation
for these values, as well as making it a great choice for libraries offering API flexibility.
This package is ESM only.
In Node.js (version 20+) with yarn:
yarn add @flex-development/whenSee Git - Protocols | Yarn for details regarding installing from Git.
In Deno with esm.sh:
import { when } from 'https://esm.sh/@flex-development/when'In browsers with esm.sh:
<script type="module">
import { when } from 'https://esm.sh/@flex-development/when'
</script>import { isThenable, when, type Awaitable } from '@flex-development/when'
import { ok } from 'devlop'
/**
* The result.
*
* @const {Awaitable<number>} result
*/
const result: Awaitable<number> = when(0, n => n + 1)
ok(!isThenable(result), 'expected `result` to not be thenable')
console.dir(result) // 1Chain a thenable
import { isThenable, when, type Awaitable } from '@flex-development/when'
import { ok } from 'devlop'
/**
* The result.
*
* @const {Awaitable<number>} result
*/
const result: Awaitable<number> = when(Promise.resolve(2), n => n + 1)
ok(isThenable(result), 'expected `result` to be thenable')
console.dir(await result) // 3Arguments are passed first, and the resolved value is passed last.
When the value passed to when is not thenable, the resolved value is the same value.
import when, { type Awaitable } from '@flex-development/when'
/**
* The result.
*
* @const {Awaitable<number>} result
*/
const result: Awaitable<number> = when(1, Math.min, null, undefined, 2, 3, 4)
console.dir(result) // 1import when, { type Awaitable } from '@flex-development/when'
/**
* The thenable value.
*
* @const {PromiseLike<never>} value
*/
const value: PromiseLike<never> = new Promise((resolve, reject) => {
return void reject(new Error('nope', { cause: { url: import.meta.url } }))
})
/**
* The result.
*
* @const {Awaitable<boolean>} result
*/
const result: Awaitable<boolean> = when(value, chain, reject)
console.dir(await result) // false
/**
* @this {void}
*
* @return {true}
* The success result
*/
function chain(this: void): true {
return true
}
/**
* @this {void}
*
* @param {Error} e
* The error to handle
* @return {false}
* The failure result
*/
function reject(this: void, e: Error): false {
return console.dir(e), false
}import when, { type Awaitable } from '@flex-development/when'
/**
* The `this` context.
*/
type Context = { prefix: string }
/**
* The result.
*
* @const {Awaitable<string>} result
*/
const result: Awaitable<string> = when(13, id, null, { prefix: 'id:' })
console.log(result) // 'id:13'
/**
* @this {Context}
*
* @param {number | string} num
* The id number
* @return {string}
* The id string
*/
function id(this: Context, num: number | string): string {
return this.prefix + num
}import when, { type Awaitable } from '@flex-development/when'
/**
* The `this` context.
*/
type Context = { errors: Error[] }
/**
* The thenable value.
*
* @const {Promise<number>} value
*/
const value: Promise<number> = new Promise(resolve => resolve(3))
/**
* The result.
*
* @const {Awaitable<number | undefined>} result
*/
const result: Awaitable<number | undefined> = when(value, {
args: [39],
chain: divide,
context: { errors: [] },
reject
})
console.dir(await result) // 13
/**
* @this {void}
*
* @param {number} dividend
* The number to divide
* @param {number} divisor
* The number to divide by
* @return {number}
* The quotient
*/
function divide(this: void, dividend: number, divisor: number): number {
return dividend / divisor
}
/**
* @this {Context}
*
* @param {Error} e
* The error to handle
* @return {undefined}
*/
function reject(this: Context, e: Error): undefined {
return void this.errors.push(e)
}when exports the identifiers listed below.
The default export is when.
Check if value looks like a thenable.
👉 Note: Also exported as
isPromiseLike.
T(any) — the resolved value
value(unknown) — the thing to check
(value is PromiseLike<T>) true if value is a thenable, false otherwise
Check if value looks like a promise.
T(any) — the resolved value
value(unknown) — the thing to check
(value is Promise<T>) true if value is a promise, false otherwise
Chain a callback, calling the function after value is resolved, or immediately if value is not thenable.
function when<
T,
Next = any,
Args extends any[] = any[],
Self = unknown
>(
this: void,
value: Awaitable<T>,
chain: Chain<T, Next, Args, Self>,
reject?: Reject<Next, any, Self> | null | undefined,
context?: Self | null | undefined,
...args: Args
): Awaitable<Next>function when<
T,
Next = any,
Args extends any[] = any[],
Self = unknown
>(
this: void,
value: Awaitable<T>,
chain: Options<T, Next, Args, Self>
): Awaitable<Next>T(any) — the previously resolved valueNext(any, optional) — the next resolved value- default:
any
- default:
Args(readonly any[], optional) — the function arguments- default:
any[]
- default:
Self(any, optional) — thethiscontext- default:
unknown
- default:
value(Awaitable<T>) — the promise or the resolved valuechain(Chain<T, Next, Args, Self>|Options<T, Next, Args, Self>) — the chain callback or options for chainingreject(Reject<Next, any, Self>|null|undefined) — the callback to fire when a promise is rejected or an error is throwncontext(Self|null|undefined) — thethiscontext of the chain and error callbacks...args(Args) — the arguments to pass to the chain callback
(Awaitable<Next>) The next promise or value
This package is fully typed with TypeScript.
A synchronous or thenable value (type).
type Awaitable<T> = PromiseLike<T> | TT(any) — the resolved value
A chain callback (type).
type Chain<
T = any,
Next = any,
Args extends readonly any[] = any[],
Self = unknown
> = (this: Self, ...params: [...Args, T]) => Awaitable<Next>T(any, optional) — the previously resolved value- default:
any
- default:
Next(any, optional) — the next resolved value- default:
any
- default:
Args(readonly any[], optional) — the function arguments- default:
any[]
- default:
Self(any, optional) — thethiscontext- default:
unknown
- default:
this(Self)...params([...Args, T]) — the function parameters, with the last being the previously resolved value. in cases where a promise is not being resolved, this is the samevaluepassed towhen
(Awaitable<Next>) The next promise or value
Options for chaining (interface).
interface Options<
T = any,
Next = any,
Args extends readonly any[] = any[],
Self = any
> { /* ... */ }T(any, optional) — the previously resolved value- default:
any
- default:
Next(any, optional) — the next resolved value- default:
any
- default:
Args(readonly any[], optional) — the chain function arguments- default:
any[]
- default:
Self(any, optional) — thethiscontext- default:
any
- default:
args?(Args|null|undefined) — the arguments to pass to thechaincallbackchain(Chain<T, Next, Args, Self>) — the chain callbackcontext?(Self|null|undefined) — thethiscontext of thechainandrejectcallbacksreject?(Reject<Next, any, Self>|null|undefined) — the callback to fire when a promise is rejected or an error is thrown
The callback to fire when a promise is rejected or an error is thrown from a synchronous function (type).
type Reject<
Next = any,
Fail = any,
Self = unknown
> = (this: Self, e: Fail) => Awaitable<Next>Next(any, optional) — the next resolved value- default:
any
- default:
Fail(any, optional) — the error to handle- default:
any
- default:
Self(any, optional) — thethiscontext- default:
unknown
- default:
this(Self)e(Fail) — the error
(Awaitable<Next>) The next promise or value
A synchronous or thenable value.
An object or function with a .then method.
JavaScript engines use duck-typing for promises.
Structures with a .then method will be treated as promise-like objects, and work with built-in mechanisms
like Promise.resolve and the await keyword like native promises.
when adheres to semver.
See CONTRIBUTING.md.
This project has a code of conduct. By interacting with this repository, organization, or community you agree to abide by its terms.