A lightweight Promises/A+ and when() implementation, plus other async goodies.
npx @tessl/cli install tessl/npm-when@3.7.0When.js is a battle-tested Promises/A+ and when() implementation that provides a comprehensive suite of asynchronous utilities for JavaScript. It offers a powerful combination of small size, high performance, and rich features for managing complex asynchronous flows through promise chaining, error handling, and control flow utilities.
npm install when// ESM
import when from "when";
import { resolve, reject, all, map } from "when";
// CommonJS
const when = require("when");
const { resolve, reject, all, map } = when;
// AMD
define(['when'], function(when) {
// use when
});// Create and transform promises
when(someValue)
.then(result => result * 2)
.catch(error => console.error('Error:', error));
// Promise creation
const promise = when.resolve("hello world");
const rejected = when.reject(new Error("failed"));
// Array processing
when.all([promise1, promise2, promise3])
.then(results => console.log('All resolved:', results));/**
* Get a trusted promise for value x, or transform x with callbacks
* @param x - Value or promise to resolve
* @param onFulfilled - Success callback
* @param onRejected - Error callback
* @param onProgress - Progress callback (deprecated)
* @returns Promise for the result
*/
function when<T, U>(
x: T | Promise<T>,
onFulfilled?: (value: T) => U | Promise<U>,
onRejected?: (reason: any) => U | Promise<U>,
onProgress?: (update: any) => void
): Promise<U>;/**
* Create a new promise with a resolver function
* @param resolver - Function that receives resolve, reject, notify callbacks
* @returns New promise
*/
function promise<T>(
resolver: (
resolve: (value: T) => void,
reject: (reason: any) => void,
notify: (progress: any) => void
) => void
): Promise<T>;
/**
* Create a resolved promise
* @param value - Value to resolve with
* @returns Promise resolved with value
*/
function resolve<T>(value: T | Promise<T>): Promise<T>;
/**
* Create a rejected promise
* @param reason - Rejection reason
* @returns Promise rejected with reason
*/
function reject<T = never>(reason: any): Promise<T>;interface Deferred<T> {
/** The underlying promise */
promise: Promise<T>;
/** Resolve the promise */
resolve: (value: T) => void;
/** Reject the promise */
reject: (reason: any) => void;
/** Notify progress (deprecated) */
notify: (progress: any) => void;
/** Resolver object with resolve/reject/notify methods */
resolver: {
resolve: (value: T) => void;
reject: (reason: any) => void;
notify: (progress: any) => void;
};
}
/**
* Create a deferred promise/resolver pair
* @returns Deferred object with promise and resolver methods
*/
function defer<T>(): Deferred<T>;// Import from when/function module
import { lift, apply, call, compose } from "when/function";
/**
* Lift a function to work with promises as arguments and return promises
* @param f - Function to lift
* @returns Promise-aware version of the function
*/
function lift<TArgs extends any[], TResult>(
f: (...args: TArgs) => TResult
): (...args: { [K in keyof TArgs]: TArgs[K] | Promise<TArgs[K]> }) => Promise<TResult>;
/**
* Apply a function with args array and return a promise for its result
* @param f - Function to apply
* @param args - Arguments array to pass to function
* @returns Promise for function result
*/
function apply<TArgs extends any[], TResult>(
f: (...args: TArgs) => TResult,
args: TArgs
): Promise<TResult>;
/**
* Call a function and return a promise for its result (alias for when.try)
* @param f - Function to call
* @param args - Arguments to pass to function
* @returns Promise for function result
*/
function call<TResult>(f: () => TResult): Promise<TResult>;
function call<T1, TResult>(f: (arg1: T1) => TResult, arg1: T1): Promise<TResult>;
/**
* Compose functions into a promise-aware pipeline
* @param f - First function to receive arguments
* @param funcs - Additional functions to compose in sequence
* @returns Composed function that returns a promise
*/
function compose<TArgs extends any[], TResult>(
f: (...args: TArgs) => any,
...funcs: ((arg: any) => any)[]
): (...args: TArgs) => Promise<TResult>;/**
* Resolve all promises in an array
* @param promises - Array of promises or values
* @returns Promise for array of resolved values
*/
function all<T>(promises: (T | Promise<T>)[]): Promise<T[]>;
/**
* Settle all promises (resolve or reject) and return outcome descriptors
* @param promises - Array of promises or values
* @returns Promise for array of settlement descriptors
*/
function settle<T>(promises: (T | Promise<T>)[]): Promise<SettleDescriptor<T>[]>;
interface SettleDescriptor<T> {
state: "fulfilled" | "rejected";
value?: T;
reason?: any;
}
/**
* Join multiple promises (variadic version of all)
* @param promises - Promise arguments to join
* @returns Promise for array of resolved values
*/
function join<T extends any[]>(...promises: { [K in keyof T]: T[K] | Promise<T[K]> }): Promise<T>;/**
* Race promises, resolving with first to settle
* @param promises - Array of promises to race
* @returns Promise that resolves/rejects with first settlement
*/
function race<T>(promises: (T | Promise<T>)[]): Promise<T>;
/**
* Resolve with first promise to fulfill (ignores rejections until all reject)
* @param promises - Array of promises
* @returns Promise for first fulfilled value
*/
function any<T>(promises: (T | Promise<T>)[]): Promise<T>;
/**
* Resolve with first N promises to fulfill
* @param promises - Array of promises
* @param count - Number of fulfillments needed
* @returns Promise for array of first N fulfilled values
*/
function some<T>(promises: (T | Promise<T>)[], count: number): Promise<T[]>;/**
* Promise-aware array map function
* @param promises - Array of promises or values
* @param mapFunc - Mapping function (may return promise)
* @returns Promise for array of mapped values
*/
function map<T, U>(
promises: (T | Promise<T>)[],
mapFunc: (value: T, index: number) => U | Promise<U>
): Promise<U[]>;
/**
* Promise-aware array filter function
* @param promises - Array of promises or values
* @param predicate - Filter predicate (may return promise)
* @returns Promise for array of filtered values
*/
function filter<T>(
promises: (T | Promise<T>)[],
predicate: (value: T, index: number) => boolean | Promise<boolean>
): Promise<T[]>;
/**
* Promise-aware array reduce function
* @param promises - Array of promises or values
* @param reducer - Reducer function (may return promise)
* @param initialValue - Initial accumulator value
* @returns Promise for final reduced value
*/
function reduce<T, U>(
promises: (T | Promise<T>)[],
reducer: (accumulator: U, value: T, index: number) => U | Promise<U>,
initialValue: U
): Promise<U>;
/**
* Promise-aware array reduceRight function
* @param promises - Array of promises or values
* @param reducer - Reducer function (may return promise)
* @param initialValue - Initial accumulator value
* @returns Promise for final reduced value
*/
function reduceRight<T, U>(
promises: (T | Promise<T>)[],
reducer: (accumulator: U, value: T, index: number) => U | Promise<U>,
initialValue: U
): Promise<U>;interface Promise<T> {
/**
* Standard Promises/A+ then method
* @param onFulfilled - Success callback
* @param onRejected - Error callback
* @param onProgress - Progress callback (deprecated)
* @returns New promise for callback result
*/
then<U>(
onFulfilled?: (value: T) => U | Promise<U>,
onRejected?: (reason: any) => U | Promise<U>,
onProgress?: (progress: any) => void
): Promise<U>;
/**
* Catch promise rejections
* @param onRejected - Error callback
* @returns New promise
*/
catch<U>(onRejected: (reason: any) => U | Promise<U>): Promise<T | U>;
/**
* Execute callback regardless of outcome
* @param onFinally - Cleanup callback
* @returns Promise with original value/reason
*/
finally(onFinally: () => void | Promise<void>): Promise<T>;
}interface Promise<T> {
/**
* Delay promise resolution
* @param ms - Delay in milliseconds
* @returns Promise that resolves after delay with same value
*/
delay(ms: number): Promise<T>;
/**
* Add timeout to promise
* @param ms - Timeout in milliseconds
* @param reason - Custom timeout reason
* @returns Promise that rejects if timeout exceeded
*/
timeout(ms: number, reason?: any): Promise<T>;
}interface PromiseInspection<T> {
/** Current state of the promise */
state: "pending" | "fulfilled" | "rejected";
/** Resolved value (only if fulfilled) */
value?: T;
/** Rejection reason (only if rejected) */
reason?: any;
}
interface Promise<T> {
/**
* Synchronously inspect promise state
* @returns Inspection object with state and value/reason
*/
inspect(): PromiseInspection<T>;
/**
* Execute side effect without changing promise value
* @param onFulfilled - Side effect callback
* @returns Promise with original value
*/
tap(onFulfilled: (value: T) => void | Promise<void>): Promise<T>;
/**
* Spread array result as function arguments
* @param onFulfilled - Callback that receives spread arguments
* @returns Promise for callback result
*/
spread<U>(onFulfilled: (...values: T extends any[] ? T : [T]) => U | Promise<U>): Promise<U>;
}// Import from when/keys module
import { all as keysAll, map as keysMap, settle as keysSettle } from "when/keys";
/**
* Resolve all properties of an object
* @param object - Object with promise or value properties
* @returns Promise for object with resolved properties
*/
function keysAll<T extends Record<string, any>>(
object: { [K in keyof T]: T[K] | Promise<T[K]> }
): Promise<T>;
/**
* Map over object properties with promise-aware function
* @param object - Object to map over
* @param mapFunc - Mapping function for values
* @returns Promise for object with mapped values
*/
function keysMap<T, U>(
object: Record<string, T | Promise<T>>,
mapFunc: (value: T, key: string) => U | Promise<U>
): Promise<Record<string, U>>;
/**
* Settle all properties of an object
* @param object - Object with promise or value properties
* @returns Promise for object with settlement descriptors
*/
function keysSettle<T extends Record<string, any>>(
object: { [K in keyof T]: T[K] | Promise<T[K]> }
): Promise<{ [K in keyof T]: SettleDescriptor<T[K]> }>;/**
* Run array of tasks in parallel with same arguments
* @param tasks - Array of task functions
* @param args - Arguments to pass to all tasks
* @returns Promise for array of task results
*/
function parallel<T extends any[], U>(
tasks: ((...args: T) => U | Promise<U>)[],
...args: T
): Promise<U[]>;/**
* Run array of tasks in sequence with same arguments
* @param tasks - Array of task functions
* @param args - Arguments to pass to all tasks
* @returns Promise for array of task results
*/
function sequence<T extends any[], U>(
tasks: ((...args: T) => U | Promise<U>)[],
...args: T
): Promise<U[]>;/**
* Run tasks in pipeline where each receives result of previous
* @param tasks - Array of task functions
* @param initialArgs - Arguments for first task
* @returns Promise for final task result
*/
function pipeline<T extends any[], U>(
tasks: [(...args: T) => any, ...((arg: any) => any)[]],
...initialArgs: T
): Promise<U>;interface CancelablePromise<T> extends Promise<T> {
/** Cancel the polling operation */
cancel(): void;
}
/**
* Poll a task until condition is met or cancelled
* @param task - Task function to poll
* @param interval - Polling interval in milliseconds
* @param verifier - Optional condition verifier
* @returns Cancelable promise for polling result
*/
function poll<T>(
task: () => T | Promise<T>,
interval: number,
verifier?: (result: T) => boolean | Promise<boolean>
): CancelablePromise<T>;// Import from when/callbacks module
import { lift, apply, call, promisify } from "when/callbacks";
/**
* Lift callback-style function to promise-returning function
* @param callbackFunc - Function expecting callback(err, result)
* @returns Promise-returning version of function
*/
function lift<TArgs extends any[], TResult>(
callbackFunc: (...args: [...TArgs, (err: any, result?: TResult) => void]) => void
): (...args: TArgs) => Promise<TResult>;
/**
* Apply callback-style function with args array and return promise
* @param callbackFunc - Function expecting callback(err, result)
* @param args - Arguments array for function
* @returns Promise for function result
*/
function apply<TArgs extends any[], TResult>(
callbackFunc: (...args: [...TArgs, (err: any, result?: TResult) => void]) => void,
args: TArgs
): Promise<TResult>;
/**
* Call callback-style function with individual arguments
* @param callbackFunc - Function expecting callback(err, result)
* @param args - Arguments for function
* @returns Promise for function result
*/
function call<TArgs extends any[], TResult>(
callbackFunc: (...args: [...TArgs, (err: any, result?: TResult) => void]) => void,
...args: TArgs
): Promise<TResult>;// Import from when/node module
import { lift, apply, call, createCallback, bindCallback, liftCallback } from "when/node";
/**
* Lift Node.js-style callback function to promise-returning function
* @param nodeFunc - Function expecting Node-style callback(err, result)
* @returns Promise-returning version of function
*/
function lift<TArgs extends any[], TResult>(
nodeFunc: (...args: [...TArgs, (err: any, result?: TResult) => void]) => void
): (...args: TArgs) => Promise<TResult>;
/**
* Apply Node.js-style function with args array and return promise
* @param nodeFunc - Function expecting Node-style callback(err, result)
* @param args - Arguments array for function
* @returns Promise for function result
*/
function apply<TArgs extends any[], TResult>(
nodeFunc: (...args: [...TArgs, (err: any, result?: TResult) => void]) => void,
args: TArgs
): Promise<TResult>;
/**
* Call Node.js-style function with individual arguments and return promise
* @param nodeFunc - Function expecting Node-style callback(err, result)
* @param args - Arguments for function
* @returns Promise for function result
*/
function call<TArgs extends any[], TResult>(
nodeFunc: (...args: [...TArgs, (err: any, result?: TResult) => void]) => void,
...args: TArgs
): Promise<TResult>;
/**
* Create a Node.js-style callback from a resolver
* @param resolver - Promise resolver with resolve/reject methods
* @returns Node-style callback function
*/
function createCallback(resolver: {
resolve: (value: any) => void;
reject: (reason: any) => void;
}): (err: any, value?: any) => void;
/**
* Bind a Node.js-style callback to a promise
* @param promise - Promise to bind callback to
* @param callback - Node-style callback function
* @returns Promise with same state as input promise
*/
function bindCallback<T>(
promise: Promise<T>,
callback: (err: any, value?: T) => void
): Promise<T>;
/**
* Lift a Node.js-style callback to accept promises
* @param callback - Node-style callback to lift
* @returns Function that accepts a promise and calls callback on resolution
*/
function liftCallback(
callback: (err: any, value?: any) => void
): (promise: Promise<any>) => Promise<any>;/**
* Check if value is thenable (promise-like)
* @param x - Value to test
* @returns True if value has then method
*/
function isPromiseLike(x: any): x is Promise<any>;/**
* Error thrown when promise times out
*/
class TimeoutError extends Error {
constructor(message?: string);
}When.js provides multiple ways to access its functionality:
// Main module exports
import when from "when";
import * as callbacks from "when/callbacks";
import * as fn from "when/function";
import * as keys from "when/keys";
import * as node from "when/node";
import parallel from "when/parallel";
import sequence from "when/sequence";
import pipeline from "when/pipeline";
import poll from "when/poll";
import delay from "when/delay";
import timeout from "when/timeout";
import guard from "when/guard";
import * as generator from "when/generator";
// ES6 Promise shim
import Promise from "when/es6-shim/Promise";when(getData())
.then(processData)
.then(saveData)
.catch(handleError)
.finally(cleanup);when.resolve(value)
.then(step1)
.catch(recoverFromStep1Error)
.then(step2)
.catch(handleFinalError);const urls = ['url1', 'url2', 'url3'];
when.map(urls, fetchUrl)
.then(results => when.all(results.map(processResponse)))
.then(processedData => console.log('All done:', processedData));when(condition)
.then(result => result ? doSomething() : doSomethingElse())
.then(handleResult);// Import from when/guard module
import guard from "when/guard";
/**
* Create guarded version of function that can only execute when condition allows
* @param condition - Function that returns promise for condition check
* @param f - Function to guard
* @returns Guarded version of function
*/
function guard<TArgs extends any[], TResult>(
condition: () => Promise<any>,
f: (...args: TArgs) => TResult | Promise<TResult>
): (...args: TArgs) => Promise<TResult>;
/**
* Create guard that limits concurrent executions to N
* @param count - Maximum number of concurrent executions
* @returns Guard condition function
*/
function guardN(count: number): () => Promise<any>;// Import from when/generator module
import { lift as liftGenerator, call as callGenerator, apply as applyGenerator } from "when/generator";
/**
* Lift a generator function to work with promises and yield
* @param generator - Generator function to lift
* @returns Promise-aware generator function
*/
function liftGenerator<TArgs extends any[], TResult>(
generator: (...args: TArgs) => Generator<any, TResult, any>
): (...args: TArgs) => Promise<TResult>;
/**
* Call generator immediately as promise-aware coroutine
* @param generator - Generator function to call
* @param args - Arguments to pass to generator
* @returns Promise for generator result
*/
function callGenerator<TArgs extends any[], TResult>(
generator: (...args: TArgs) => Generator<any, TResult, any>,
...args: TArgs
): Promise<TResult>;
/**
* Apply generator with args array as promise-aware coroutine
* @param generator - Generator function to apply
* @param args - Arguments array for generator
* @returns Promise for generator result
*/
function applyGenerator<TArgs extends any[], TResult>(
generator: (...args: TArgs) => Generator<any, TResult, any>,
args: TArgs
): Promise<TResult>;// Import from when/delay (deprecated - use promise.delay() instead)
import delay from "when/delay";
/**
* Create promise that resolves after delay
* @deprecated Use when(value).delay(ms) instead
* @param ms - Delay in milliseconds
* @param value - Value to resolve with after delay
* @returns Promise that resolves after delay
*/
function delay<T>(ms: number, value?: T): Promise<T>;// Import from when/timeout (deprecated - use promise.timeout() instead)
import timeout from "when/timeout";
/**
* Create promise that times out
* @deprecated Use when(promise).timeout(ms) instead
* @param ms - Timeout in milliseconds
* @param promise - Promise to add timeout to
* @returns Promise that rejects if timeout exceeded
*/
function timeout<T>(ms: number, promise: Promise<T>): Promise<T>;// Import from when/unfold (deprecated - use cujojs/most streams instead)
import unfold from "when/unfold";
import { unfold as unfoldList } from "when/unfold/list";
/**
* Generate promise stream from generator function
* @deprecated Use cujojs/most streams instead
* @param generator - Function that generates values
* @param condition - Condition function to stop generation
* @param seed - Initial seed value
* @returns Promise for generated sequence
*/
function unfold<T, U>(
generator: (seed: T) => U | Promise<U>,
condition: (value: U, seed: T) => boolean | Promise<boolean>,
seed: T
): Promise<U[]>;