| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
like .then, but for synchronous values and thenables.
when is a tiny primitive for chaining callbacks onto awaitables (synchronous or thenable values).
For thenable values, then is used to invoke the callback after resolution. Otherwise, the callback fires immediately. This makes it easy to write one code path that supports both synchronous values and promises.
when is especially useful in libraries implementing awaitable APIs.
It provides Promise.prototype.then semantics without forcing Promise.resolve, preserving synchronous execution whenever possible.
Typical use cases include plugin systems, hook pipelines, module resolvers, data loaders, and file system adapters where users may return both synchronous or asynchronous values.
If you're only dealing with promises and thenables, consider using native async/await or Promise chaining instead.
when preserves synchronous operations whenever possible, avoiding unnecessary promise allocation and microtask scheduling.
Promise.resolve(value).then(fn) // always a promisewhen(value, fn) // only a promise if `value` is a thenable, or `fn` returns oneThis 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 } from '@flex-development/when'
import { ok } from 'devlop'
/**
* The result.
*
* @const {number} result
*/
const result: number = when(0, n => n + 1)
ok(!isThenable(result), 'expected `result` to not be thenable')
console.dir(result) // 1import { isPromise, when } from '@flex-development/when'
import { ok } from 'devlop'
/**
* The result.
*
* @const {Promise<number>} result
*/
const result: Promise<number> = when(Promise.resolve(2), n => n + 1)
ok(isPromise(result), 'expected `result` to be a promise')
console.dir(await result) // 3When arguments are provided, they are passed to the chain callback first, followed by the resolved value.
When the value passed to when is not a thenable, the resolved value is the same value.
import when from '@flex-development/when'
/**
* The result.
*
* @const {number} result
*/
const result: number = when(
1, // last argument passed to `Math.min`
Math.min, // `chain`
null, // `fail`
undefined, // `context`
2, // first argument passed to `Math.min`
3, // second argument passed to `Math.min`
4 // third argument passed to `Math.min`
)
console.dir(result) // 1For thenables, the fail callback is passed to then as the onrejected parameter, and if implemented, to catch as well to prevent unhandled rejections.
import when 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 {Promise<boolean>} result
*/
const result: Promise<boolean> = when(value, chain, fail)
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 fail(this: void, e: Error): false {
return console.dir(e), false
}import when from '@flex-development/when'
/**
* The `this` context.
*/
type Context = { prefix: string }
/**
* The result.
*
* @const {string} result
*/
const result: 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
}/**
* 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 {Promise<number | undefined>} result
*/
const result: Promise<number | undefined> = when(value, {
args: [39],
chain: divide,
context: { errors: [] },
fail
})
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 fail(this: Context, e: Error): undefined {
return void this.errors.push(e)
}/**
* The eslint configuration.
*
* @type {import('eslint').Linter.Config[]}
* @const config
*/
const config = [
{
files: ['**/*.+(cjs|cts|js|jsx|mjs|mts|ts|tsx)'],
rules: {
'@typescript-eslint/promise-function-async': [
2,
{
allowedPromiseNames: ['Thenable']
}
]
}
}
]
export default configwhen exports the identifiers listed below.
The default export is when.
Check if value looks like a Thenable that can be caught.
(value is Catchable<T>) true if value is a thenable with a catch method, false otherwise
Check if value looks like a Thenable that can be finalized.
(value is Finalizable<T>) true if value is a thenable with a finally method, false otherwise
Check if value looks like a Promise.
👉 Note: This function intentionally performs structural checks instead of brand checks. It does not rely on instanceof Promise or constructors, making it compatible with cross-realm promises and custom thenables.
(value is Promise<T>) true if value is a thenable with a catch method, and finally method (if requested), false otherwise
Check if value looks like a PromiseLike structure.
(value is PromiseLike<T>) true if value is an object or function with a then method, false otherwise
Check if value looks like a thenable.
(value is Thenable<T>) true if value is an object or function with a then method, and maybe-callable methods catch and/or finally, false otherwise
Chain a callback, calling the function after value is resolved, or immediately if value is not a thenable.
function when<
T,
Next = any,
Args extends any[] = any[],
This = unknown,
Result extends Awaitable<Next> = Awaitable<Next>
>(
this: void,
value: Awaitable<T>,
chain: Chain<T, Next, Args, This>,
fail?: null | undefined,
context?: This | null | undefined,
...args: Args
): Resultfunction when<
T,
Next = any,
Failure = Next,
Args extends any[] = any[],
Error = any,
This = unknown,
Result extends Awaitable<Failure | Next> = Awaitable<Failure | Next>
>(
this: void,
value: Awaitable<T>,
chain: Chain<T, Next, Args, This>,
fail?: Fail<Failure, Error, This> | null | undefined,
context?: This | null | undefined,
...args: Args
): Resultfunction when<
T,
Next = any,
Failure = Next,
Args extends any[] = any[],
Error = any,
This = unknown,
Result extends Awaitable<Failure | Next> = Awaitable<Failure | Next>
>(
this: void,
value: Awaitable<T>,
chain: Options<T, Next, Failure, Args, Error, This>
): Result👉 note: for thenables, this callback is passed to then as the onrejected parameter, and if implemented, to catch as well to prevent unhandled rejections.
(Awaitable<Failure | Next> | Awaitable<Next>) The next awaitable
Test utilities are exported from @flex-development/when/testing.
There is no default export.
import {
isCatchable,
isFinalizable,
type Thenable
} from '@flex-development/when'
import { createThenable } from '@flex-development/when/testing'
import { ok } from 'devlop'
/**
* The thenable.
*
* @const {Thenable<number>} thenable
*/
const thenable: Thenable<number> = createThenable(resolve => resolve(10))
ok(isCatchable(thenable), 'expected `thenable` to be a catchable')
ok(isFinalizable(thenable), 'expected `thenable` to be a finalizable')
console.dir(await thenable.then(value => value + 3)) // 13Create a thenable.
The returned object conforms to Thenable and ensures then always returns another Thenable, even when adopting a foreign thenable.
When options is omitted, null, or undefined, the returned thenable is modern (a thenable with then, catch, and finally methods). Pass an options object (e.g. {}) to start from a bare (then method only) thenable and selectively enable methods.
(Result) The thenable
This package is fully typed with TypeScript.
A synchronous or thenable value (type).
type Awaitable<T> = Thenable<T> | TAttach a callback only for the rejection of a Thenable (type).
type Catch<T = unknown, Reason = any> = <Next = never>(
this: any,
onrejected?: OnRejected<Next, Reason> | null | undefined
) => Thenable<Next | T>(Thenable<Next | T>) The next thenable
A Thenable that can be caught (interface).
A chain callback (type).
type Chain<
T = any,
Next = any,
Args extends readonly any[] = any[],
This = unknown
> = (this: This, ...params: [...Args, T]) => Awaitable<Next>(Awaitable<Next>) The next awaitable
Options for creating a thenable (interface).
👉 Note: Exported from @flex-development/when/testing only.
The callback used to initialize a thenable (type).
👉 Note: Exported from @flex-development/when/testing only.
type Executor<T = any, Reason = Error> = (
this: void,
resolve: Resolve<T>,
reject: Reject<Reason>
) => undefined | void(undefined | void) Nothing
The callback to fire when a failure occurs (type).
type Fail<
Next = any,
Reason = any,
This = unknown
> = (this: This, reason: Reason) => Awaitable<Next>(Awaitable<Next>) The next awaitable
A Thenable that can be finalized (interface).
👉 note: the resolved value cannot be modified from the callback
Attach a callback that is invoked only when a Thenable is settled (fulfilled or rejected) (type).
type Finally<T = unknown> = (
this: any,
onfinally?: OnFinally | null | undefined
) => Thenable<T>(Thenable<T>) The next thenable
A post-processing hook invoked exactly once after an awaitable settles, regardless of success or failure (type).
The resolved value cannot be modified from the hook, and any error is re-thrown after execution.
type Finish<This = unknown> = (this: This) => undefined | void(undefined | void) Nothing
The callback to execute when a Thenable is settled (fulfilled or rejected) (type).
type OnFinally = (this: unknown) => undefined | void(undefined | void) Nothing
The callback to execute when a Thenable is resolved (type).
type OnFulfilled<T, Next = T> = (this: unknown, value: T) => Awaitable<Next>(Awaitable<Next>) The next awaitable
The callback to execute when a Thenable is rejected (type).
type OnRejected<
Next,
Reason = any
> = (this: unknown, reason: Reason) => Awaitable<Next>(Awaitable<Next>) The next awaitable
Options for chaining (interface).
interface Options<
T = any,
Next = any,
Failure = Next,
Args extends readonly any[] = any[],
Error = any,
This = any
> { /* ... */ }👉 note: for thenables, this callback is passed to then as the onrejected parameter, and if implemented, to catch as well to prevent unhandled rejections.
To ensure native Promise and PromiseLike are assignable to Thenable, when ships a small global augmentation for PromiseLike.
No new methods or overloads are introduced — the then signature is rewritten to match the official TypeScript lib definition (as in lib.es2015.d.ts).
This is required for both compatibility, and type inference when mixing Thenable with built-in promise types.
The callback used to reject a thenable with a provided reason or error (type).
👉 Note: Exported from @flex-development/when/testing only.
type Reject<Reason = Error> = (this: void, reason: Reason) => undefined(undefined) Nothing
The callback used to resolve a thenable with a value or the result of another awaitable (type).
👉 Note: Exported from @flex-development/when/testing only.
type Resolve<T = any> = (this: void, value: Awaitable<T>) => undefined(undefined) Nothing
Attach callbacks for the resolution and/or rejection of a Thenable (type).
type Then<T = unknown, Reason = any> = <Succ = T, Fail = never>(
this: any,
onfulfilled?: OnFulfilled<T, Succ> | null | undefined,
onrejected?: OnRejected<Fail, Reason> | null | undefined
) => Thenable<Fail | Succ>(Thenable<Fail | Succ>) The next thenable
The completion of an asynchronous operation, and the minimal structural contract required by when to treat a value as asynchronous (interface).
Unlike PromiseLike, this interface allows a maybe-callable catch method, which when present, is used by when to ensure failures are handled without forcing promise allocation.
Maybe-callable methods are named so because they are not required, and may be a method implementation, null, or undefined.
👉 note: the resolved value cannot be modified from the callback
A synchronous or thenable value.
An object or function with a then method.
JavaScript engines use duck-typing for promises. Arrays, functions, and objects 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.
Some thenables also implement a catch method (like native promises). When available, when uses it to ensure rejections are handled.
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.
This package is intentionally small — and intentionally maintained.
Small primitives power larger systems. Support long-term stability by sponsoring Flex Development.
| Back | FazBrowse Home | New Git URL |