Skip to content

Latest commit

 

History

History
2342 lines (1663 loc) · 83.3 KB

api.md

File metadata and controls

2342 lines (1663 loc) · 83.3 KB

API

  1. Core
  2. Promise
  3. Arrays
  4. Array Races
  5. Infinite Promise Sequences
  6. Objects
  7. Functions
  8. ES6 generators
  9. Task Execution
  10. Limiting Concurrency
  11. Error types
  12. Debugging promises
  13. Upgrading to 3.0 from 2.x
  14. Refactoring progress

Core

when()

var promise = when(x);

Get a trusted promise for x. If x is:

  • a value, returns a promise fulfilled with x
  • a promise, returns x.
  • a foreign thenable, returns a promise that follows x
var transformedPromise = when(x, f);

Get a trusted promise by transforming x with f. If x is

  • a value, returns a promise fulfilled with f(x)
  • a promise or thenable, returns a promise that
    • if x fulfills, will fulfill with the result of calling f with x's fulfillment value.
    • if x rejects, will reject with the same reason as x

when() accepts any promise that provides a thenable promise--any object that provides a .then() method, even promises that aren't fully Promises/A+ compliant, such as jQuery's Deferred. It will assimilate such promises and make them behave like Promises/A+.

when.try

ALIAS: when.attempt() for non-ES5 environments

var promise = when.try(f /*, arg1, arg2, ...*/);

Calls f with the supplied arguments, returning a promise for the result. The arguments may be promises, in which case, f will be called after they have fulfilled. The returned promise will fulfill with the successful result of calling f. If any argument is a rejected promise, or if f fails by throwing or returning a rejected promise, the returned promise will also be rejected.

This can be a great way to kick off a promise chain when you want to return a promise, rather than creating a one manually.

// Try to parse the JSON, capture any failures in the returned promise
// (This will never throw)
return when.try(JSON.parse, jsonString);

See also:

when.lift

var g = when.lift(f);

Creates a "promisified" version of f, which always returns promises (g will never throw) and accepts promises or values as arguments. In other words, calling g(x, y z) is like calling when.try(f, x, y, z) with the added convenience that once you've created g you can call it repeatedly or pass it around like any other function. In addition, g's thisArg will behave in a predictable way, like any other function (you can .bind() it, or use .call() or .apply(), etc.).

Like when.try, lifting functions provides a convenient way start promise chains without having to explicitly create promises, e.g. new Promise

// Call parse as often as you need now.
// It will always return a promise, and will never throw
// Errors will be captured in the returned promise.
var jsonParse = when.lift(JSON.parse);

// Now use it wherever you need
return jsonParse(jsonString);

when.lift correctly handles this, so object methods can be lifted as well:

var parser = {
	reviver: ...
	parse: when.lift(function(str) {
		return JSON.parse(str, this.reviver);
	});
};

// Now use it wherever you need
return parser.parse(jsonString);

See also:

when.join

var joinedPromise = when.join(promiseOrValue1, promiseOrValue2, ...);

Return a promise that will fulfill only once all the inputs have fulfilled. The value of the returned promise will be an array containing the values of each of the inputs.

If any of the input promises is rejected, the returned promise will be rejected with the reason from the first one that is rejected.

// largerPromise will resolve to the greater of two eventual values
var largerPromise = when.join(promise1, promise2).then(function (values) {
	return values[0] > values[1] ? values[0] : values[1];
});

See also:

  • when.all - resolving an Array of promises

when.promise

var promise = when.promise(resolver);

Create a Promise, whose fate is determined by running the supplied resolver function. The resolver function will be called synchronously, with 3 arguments:

var promise = when.promise(function(resolve, reject, notify) {
	// Do some work, possibly asynchronously, and then
	// resolve or reject.

	// DEPRECATED: You can notify of progress events
	// along the way if you want/need.

	resolve(awesomeResult);
	// or resolve(anotherPromise);
	// or reject(nastyError);
});
  • resolve(promiseOrValue) - Primary function that seals the fate of the returned promise. Accepts either a non-promise value, or another promise.
    • When called with a non-promise value, fulfills promise with that value.
    • When called with another promise, e.g. resolve(otherPromise), promise's fate will be equivalent to that that of otherPromise.
  • reject(reason) - function that rejects promise.
  • notify(update) - DEPRECATED function that issues progress events for promise. See Refactoring progress for more info.

when.resolve

var resolved = when.resolve(x);

Get a promise for the supplied x. If x is already a trusted promise, it is returned. If x is a value, the returned promise will be fulfilled with x. If x is a thenable, the returned promise will follow x, adopting its eventual state (fulfilled or rejected).

when.reject

var rejected = when.reject(error);

Create a rejected promise with the supplied error as the rejection reason.

DEPRECATION WARNING: In when.js 2.x, error is allowed to be a promise for an error. In when.js 3.0, error will always be used verbatim as the rejection reason, even if it is a promise.

when.defer

var deferred = when.defer();

Note: The use of when.defer is discouraged. In most cases, using when.promise, when.try, or when.lift provides better separation of concerns.

Create a {promise, resolve, reject, notify} tuple. In certain (rare) scenarios it can be convenient to have access to both the promise and it's associated resolving functions.

The deferred API:

var promise = deferred.promise;

// Resolve the promise, x may be a promise or non-promise
deferred.resolve(x)

// Reject the promise with error as the reason
deferred.reject(error)

// DEPRECATED Notify promise consumers of a progress update
deferred.notify(x)

Note that resolve, reject, and notify all become no-ops after either resolve or reject has been called the first time.

One common use case for creating a deferred is adapting callback-based functions to promises. In those cases, it's preferable to use the when/callbacks module to call or lift the callback-based functions instead. For adapting node-style async functions, use the when/node module.

when.isPromiseLike

var is = when.isPromiseLike(x);

Return true if x is an object or function with a then method. It does not distinguish trusted when.js promises from other "thenables" (e.g. from some other promise implementation).

Using isPromiseLike is discouraged. In cases where you have an x and don't know if it's a promise, typically the best thing to do is to cast it: var trustedPromise = when(x); and then use trustedPromise.

Promise

A promise is a proxy for a value that isn't available yet allowing you to interact with it as if it is.

promise.done

promise.done(handleValue); // returns undefined

The simplest API for interacting with a promise, done consumes the promise's ultimate value if it fulfills, or causes a fatal error with a loud stack trace if it rejects.

promise.done(handleValue, handleError); // returns undefined

Consume the promise's ultimate value if the promise fulfills, or handle the ultimate error. It will cause a fatal error if either handleValue or handleError throw or return a rejected promise.

Since done's purpose is consumption rather than transformation, done always returns undefined.

One golden rule of promise error handling is:

Either return the promise, thereby passing the error-handling buck to the caller, or call done and assuming responsibility for errors.

See also

promise.then

var transformedPromise = promise.then(onFulfilled);

Promises/A+ then. Transforms a promise's value by applying a function to the promise's fulfillment value. Returns a new promise for the transformed result.

var transformedPromise = promise.then(onFulfilled, onRejected);

then may also be used to recover from intermediate errors. However, promise.catch is almost always a better, and more readable choice. When onRejected is provided, it only handles errors from promise, and will not handle errors thrown by onFulfilled. Compare:

// Using only then(): onRejected WILL NOT handle errors thrown by onFulfilled
var transformedPromise = promise
	.then(onFulfilled, onRejected);

// Using catch(): onRejected will handled errors thrown by onFulfilled
var transformedPromise = promise
	.then(onFulfilled)
	.catch(onRejected);

// Using catch() is equivalent to:
var transformedPromise = promise
	.then(onFulfilled)
	.then(void 0, onRejected);

DEPRECATED: Progress events are deprecated and will be removed in a future release. Until that release. See Refactoring progress.

// Deprecated use of then() and promise.progress() to listen for progress events

var transformedPromise = promise.then(onFulfilled, onRejected, onProgress);
// or
var transformedPromise = promise
	.progress(onProgress)
	.then(onFulfilled)
	.catch(onRejected)

then arranges for:

  • onFulfilled to be called with the value after promise is fulfilled, or
  • onRejected to be called with the rejection reason after promise is rejected.
  • DEPRECATED: onProgress to be called with any progress updates issued by promise. See Refactoring progress.

A promise makes the following guarantees about handlers registered in the same call to .then():

  1. Only one of onFulfilled or onRejected will be called, never both.
  2. onFulfilled and onRejected will never be called more than once.
  3. onProgress may be called zero or more times.

See also

promise.spread

var transformedPromise = promise.spread(onFulfilledArray);

Similar to then, but calls onFulfilledArray with promise's value, which is assumed to be an array, as its argument list. It will also deeply resolve promises within the array.

It's equivalent to:

// Wrapping onFulfilledArray
when.all(promise).then(function(array) {
	return onFulfilledArray.apply(undefined, array);
});

See also

promise.fold

var resultPromise = promise2.fold(combine, promise1)

Combine promise1 and promise2 to produce a resultPromise. The combine function will be called once both promise1 and promise2 have fulfilled: combine(promise1, promise2), and like then et al, it may return a promise or a value.

Just as promise.then allows you to easily re-use existing one-argument functions to transform promises, promise.fold allows you to reuse two-argument functions. It can also be useful when you need to thread one extra piece of information into a promise chain, without having to capture it in a closure or use promise.with.

For, example, with an existing sum function, you can easily sum the value of two promises:

function sum(x, y) {
	return x + y;
}

var promiseFor3 = when(3);

var promiseFor5 = promiseFor3.fold(sum, promiseFor2);

// Of course, it accepts values as well:
var promiseFor5 = promiseFor3.fold(sum, 2);

Or get object properties or array values:

function get(key, object) {
	return object[key];
}

when({ name: 'Bob' })
	.fold(get, 'name')
	.done(console.log); // logs 'Bob'

when(['a', 'b', 'c'])
	.fold(get, 1)
	.done(console.log); // logs 'b'

In both cases, sum and get are generic, reusable functions, and no closures were required.

promise.catch

ALIAS: otherwise() for non-ES5 environments

var recoveredPromise = promise.catch(onRejected);

In it's simplest form, catch arranges to call onRejected on the promise's rejection reason if it is rejected.

var recoveredPromise = promise.catch(predicate, onRejected);

If you also supply a predicate, you can catch only errors matching the predicate. This allows much more precise error handling. The predicate can be either an Error constructor, like TypeError, ReferenceError, or any custom error type (its prototype must be instanceof Error), or it can be a function that returns a boolean.

promise.then(function() {
	throw new CustomError('oops!');
}).catch(CustomError, function(e) {
	// Only catch CustomError instances
	// all other types of errors will propagate automatically
}).catch(function(e) {
	// Catch other errors
})

Doing this in synchronous code is more clumsy, requiring instanceof checks inside a catch block:

try {
	throw new CustomError('oops!');
} catch(e) {
	if(e instanceof CustomError) {
		// Handler CustomError instances
	} else {
		// Handle other errors
	}
}

See also:

promise.finally

ALIAS: ensure() for non-ES5 environments

var promise2 = promise1.finally(cleanup);

Finally allows you to execute "cleanup" type tasks in a promise chain. It arranges for cleanup to be called, with no arguments, when promise1 is either fulfilled or rejected. It behaves similarly the synchronous finally statement:

  • If promise1 fulfills, and cleanup returns successfully, promise2 will fulfill with the same value as promise1.
  • If promise1 rejects, and cleanup returns successfully, promise2 will reject with the same reason as promise1.
  • If promise1 rejects, and cleanup throws or returns a rejected promise, promise2 will reject with the thrown exception or rejected promise's reason.

When combined with promise.catch, promise.finally allows you to write code that is similar to the familiar synchronous catch/finally pair. Consider the following synchronous code:

try {
  return doSomething(x);
} catch(e) {
	return handleError(e);
} finally {
	cleanup();
}

Using promise.finally, similar asynchronous code (with doSomething() that returns a promise) can be written:

return doSomething()
	.catch(handleError)
	.finally(cleanup);

See also:

promise.yield

originalPromise.yield(promiseOrValue);

Returns a new promise:

  1. If originalPromise is rejected, the returned promise will be rejected with the same reason
  2. If originalPromise is fulfilled, then it "yields" the resolution of the returned promise to promiseOrValue, namely:
    1. If promiseOrValue is a value, the returned promise will be fulfilled with promiseOrValue
    2. If promiseOrValue is a promise, the returned promise will be:
      • fulfilled with the fulfillment value of promiseOrValue, or
      • rejected with the rejection reason of promiseOrValue

In other words, it's much like:

originalPromise.then(function() {
	return promiseOrValue;
});

See also:

  • promise.else - return a default value when promise rejects

promise.else

ALIAS: orElse() for non-ES5 environments

var p1 = doAsyncOperationThatMightFail();
return p1.else(defaultValue);

If a promise is rejected, else catches the rejection and resolves the returned promise with a default value. This is a shortcut for manually catching a promise and returning a different value, as such:

var p1 = doAsyncOperationThatMightFail();
return p1.catch(function() {
    return defaultValue;
});

See also:

promise.tap

var promise2 = promise1.tap(onFulfilledSideEffect);

Executes a function as a side effect when promise fulfills. Returns a new promise:

  1. If promise fulfills, onFulfilledSideEffect is executed:
    • If onFulfilledSideEffect returns successfully, the promise returned by tap fulfills with promise's original fulfillment value. That is, onfulfilledSideEffect's result is discarded.
    • If onFulfilledSideEffect throws or returns a rejected promise, the promise returned by tap rejects with the same reason.
  2. If promise rejects, onFulfilledSideEffect is not executed, and the promise returned by tap rejects with promise's rejection reason.

These are equivalent:

// Using only .then()
promise.then(function(x) {
	doSideEffectsHere(x);
	return x;
});

// Using .tap()
promise.tap(doSideEffectsHere);

See also:

promise.delay

var delayedPromise = promise.delay(milliseconds);

Create a new promise that will, after milliseconds delay, fulfill with the same value as promise. If promise rejects, delayedPromise will be rejected immediately.

var delayed;

// delayed is a pending promise that will become fulfilled
// in 1 second with the value "hello"
delayed = when('hello').delay(1000);

// delayed is a pending promise that will become fulfilled
// 1 second after anotherPromise resolves, or will become rejected
// *immediately* after anotherPromise rejects.
delayed = promise.delay(1000);

// Do something 1 second after promise resolves
promise.delay(1000).then(doSomething).catch(handleRejection);

See also:

promise.timeout

var timedPromise = promise.timeout(milliseconds, reason);

Create a new promise that will reject with a TimeoutError or a custom reason after a timeout if promise does not fulfill or reject beforehand.

var node = require('when/node');

// Lift fs.readFile so it returns promises
var readFile = node.lift(fs.readFile);

// Try to read the file, but timeout if it takes too long
function readWithTimeout(path) {
	return readFile(path).timeout(500);
}

You can pattern-match using catch to specifically handle TimeoutErrors:

var when = require('when');

var p = readWithTimeout('/etc/passwd')
	.catch(when.TimeoutError, handleTimeout) // handle only TimeoutError
	.catch(handleFailure) // handle other errors

See also:

promise.inspect

var status = promise.inspect();

Returns a snapshot descriptor of the current state of promise. This descriptor is not live and will not update when promise's state changes. The descriptor is an object with the following properties. When promise is:

  • pending: { state: 'pending' }
  • fulfilled: { state: 'fulfilled', value: <promise's fulfillment value> }
  • rejected: { state: 'rejected', reason: <promise's rejection reason> }

While there are use cases where synchronously inspecting a promise's state can be helpful, the use of inspect is discouraged. It is almost always preferable to simply use when() or promise.then to be notified when the promise fulfills or rejects.

See also:

promise.with

ALIAS: withThis() for non-ES5 environments

var boundPromise = promise.with(object);

Creates a new promise that follows promise, but which will invoke its handlers with their this set to object. Normally, promise handlers are invoked with no specific thisArg, so with can be very useful when bridging promises to object-oriented patterns and libraries.

NOTE: Promises returned from with/withThis are NOT Promises/A+ compliant, specifically violating 2.2.5 (http://promisesaplus.com/#point-41)

For example:

function Thing(value, message) {
	this.value = value;
	this.message = message;
}

Thing.prototype.doSomething = function(x) {
	var promise = doAsyncStuff(x);
	return promise.with(this) // Set thisArg to this thing instance
		.then(this.addValue)  // Works since addValue will have correct thisArg
		.then(this.format);   // all subsequent promises retain thisArg
};

Thing.prototype.addValue = function(y) {
	return this.value + y;
};

Thing.prototype.format = function(result) {
	return this.message + result;
};

// Using it
var thing = new Thing(41, 'The answer is ');

thing.doSomething(1)
    .with(console) // Re-bind thisArg now to console
	.then(console.log); // Logs 'The answer is 42'

All promises created from boundPromise will also be bound to the same thisArg until with is used to re-bind or unbind it. In the previous example, the promise returned from thing.doSomething still has its thisArg bound to thing. That may not be what you want, so you can unbind it just before returning:

Thing.prototype.doSomething = function(x) {
	var promise = doAsyncStuff(x);
	return promise.with(this)
		.then(this.addValue)
		.then(this.format)
		.with(); // Unbind thisArg
};

promise.progress

DEPRECATED Progress events are deprecated. See Refactoring progress

promise.progress(onProgress);

Registers a handler for progress updates from promise. It is a shortcut for:

promise.then(void 0, void 0, onProgress);

Notes on Progress events

Progress events are not specified in Promises/A+ and are optional in Promises/A. They have proven to be useful in practice, but unfortunately, they are also underspecified, and there is no current de facto or agreed-upon behavior in the promise implementor community.

In when.js, progress events will be propagated through a promise chain:

  1. In the same way as resolution and rejection handlers, your progress handler MUST return a progress event to be propagated to the next link in the chain. If you return nothing, undefined will be propagated.
  2. Also in the same way as resolutions and rejections, if you don't register a progress handler (e.g. .then(handleResolve, handleReject /* no progress handler */)), the update will be propagated through.
  3. This behavior will likely change in future releases: If your progress handler throws an exception, the exception will be propagated to the next link in the chain. The best thing to do is to ensure your progress handlers do not throw exceptions.
    1. Known Issue: If you allow an exception to propagate and there are no more progress handlers in the chain, the exception will be silently ignored. We're working on a solution to this.

This gives you the opportunity to transform progress events at each step in the chain so that they are meaningful to the next step. It also allows you to choose not to transform them, and simply let them propagate untransformed, by not registering a progress handler.

Here is an example:

function myProgressHandler(update) {
	logProgress(update);
	// Return a transformed progress update that is
	// useful for progress handlers of the next promise!
	return update + 1;
}

function myOtherProgressHandler(update) {
	logProgress(update);
}

var d = when.defer();
d.promise.then(undefined, undefined, myProgressHandler);

var chainedPromise = d.promise.then(doStuff);
chainedPromise.then(undefined, undefined, myOtherProgressHandler);

var update = 1;
d.notify(update);

// Results in:
// logProgress(1);
// logProgress(2);

Arrays

when.all

var promise = when.all(array)

Where:

  • array is an Array or a promise for an array, which may contain promises and/or values.

Return a promise that will resolve only once all the items in array have resolved. The resolution value of the returned promise will be an array containing the resolution values of each of the items in array.

If any of the promises is rejected, the returned promise will be rejected with the rejection reason of the first promise that was rejected.

See also:

when.map

var promise = when.map(array, mapper)

Where:

  • array is an Array or promise for an Array, which may contain promises and/or values

Traditional array map function, similar to Array.prototype.map(), but allows input to contain promises and/or values, and mapFunc may return either a value or a promise. The order of items in the input array and the results will match, however, when.map allows mapping to proceed opportunistically as promises in the array fulfill, making it extremely efficient.

If any of the promises is rejected, the returned promise will be rejected with the rejection reason of the first promise that was rejected.

The map function should have the signature:

mapFunc(value:*, index:Number):*

Where:

  • value fulfilled value
  • index array index of value

when.filter

var promise = when.filter(array, predicate);

Where:

  • array is an Array or promise for an Array, which may contain promises and/or values

Filters the input array, returning a promise for the filtered array. The filtering predicate may return a boolean or promise for boolean.

If any of the promises is rejected, the returned promise will be rejected with the rejection reason of the first promise that was rejected.

The predicate should have the signature:

predicate(value:*, index:Number):boolean

Where:

  • value fulfilled value
  • index array index of value

when.reduce

when.reduceRight

var promise = when.reduce(array, reducer [, initialValue])
var promise = when.reduceRight(array, reducer [, initialValue])

Where:

  • array is an Array or a promise for an array, which may contain promises and/or values.

Traditional array reduce function, similar to Array.prototype.reduce() and Array.prototype.reduceRight(), but input may contain promises and/or values, and reduceFunc may return either a value or a promise, and initialValue may be a promise for the starting value. Both when.reduce and when.reduceRight proceed in index order (ascending or descending, respectively), without any overlapping--in contrast to when.map which proceeds opportunistically.

The reduce function should have the signature: