back to Overview

Promise

Promises are objects that represent the future result of an asynchronous operation.
Available in all modules.

Description

Promises are objects that represent the future result of an asynchronous operation. When you start such an operation, using $.request(), animate(), or wait(), you will get a Promise object that allows you to get the result as soon as the operation is finished.

Minified's full distribution ships with a Promises/A+-compliant implementation of Promises that should be able to interoperate with most other Promises implementations. Minified's Web module in stand-alone distribution comes with a limited implementation. See below for details.

What may be somewhat surprising about this Promises specification is that the only standard-compliant way to access the result is to register callbacks. They will be invoked as soon as the operation is finished. If the operation already ended when you register the callbacks, the callback will then just be called from the event loop as soon as possible (but never while the then() you register them with is still running).
This design forces you to handle the operation result asynchronously and disencourages 'bad' techniques such as polling.

The central method of a Promise, and indeed the only required function in Promises/A+, is then(). It allows you to register two callback methods, one for success (called 'fulfillment' in Promises/A+ terminology) and one for failures (called 'rejection' in Promises/A+).

This example shows you how to use then():

$.request('get', 'http://example.com/weather?zip=90210')
 .then(function success(result) {
     alert('The weather is ' + result);
 }, function error(exception) {
 	alert('Something went wrong');
 });

What makes Promises so special is that then() itself returns a new Promise, which is based on the Promise then() was called on, but can be modified by the outcome of callbacks. Both arguments to then() are optional, and you can also write the code like this:

$.request('get', 'http://example.com/weather?zip=90210')
 .then(function success(result) {
     alert('The weather is ' + result);
 })
 .then(null, function error(exception) {
 	alert('Something went wrong');
 });

Because the first then() returns a new Promise based on the original Promise, the second then() will handle errors of the request just like the first one did. There is only one subtle difference in the second example: the error handler will not only be called if the request failed, but also when the request succeded but the success handler threw an exception. That's one of the two differences between the original Promise and the Promise returned by then(). Any exception thrown in a callback causes the new Promise to be in error state.

Before I show you the second difference between the original Promise and the new Promise, let me make the example a bit more readable by using error(), which is not part of Promises/A+, but a simple extension by Minified. It just registers the failure callback without forcing you to specify null as first argument:

$.request('get', 'http://example.com/weather?zip=90210')
 .then(function success(result) {
     alert('The weather is ' + result);
 })
 .error(function error(exception) {  // error(callback) is equivalent to then(null, callback)
 	alert('Something went wrong');
 });

A very powerful capability of Promises is that you can easily chain them. If a then() callback returns a value, the new Promise returned by then() will be marked as success (fulfilled) and this value is the result of the operation. If a callback returns a Promise, the new Promise will assume the state of the returned Promise. You can use the latter to create chains of asynchronous operations, but you still need only a single error handler for all of them and you do not need to nest functions to achieve this:

$.request('get', 'http://example.com/zipcode?location=Beverly+Hills,+CA')
 .then(function(resultZip) {
     return $.request('get', 'http://example.com/weather', {zip: resultZip});
 })
 .then(function(resultWeather) {
     alert('The weather in Beverly Hills is ' + resultWeather);
 })
 .error(function(exception) {
 	alert('Something went wrong');
 });

Only the full Minified distribution allows you to create promises yourself, using the promise() function. The Promises/A+ specification does not specify how to fulfill a promise, but in Minified's implementation every Promise object has a function fire() that needs to be called when the promise result is ready. It requires two arguments. The first is a boolean, true for a successful operation and false for a failure. The second is an array or list containing the arguments to call the corresponding then() handler with.

The following example is a function, similar to wait(), that returns a Promise which succeeds after the given amount of milliseconds has passed. It then fulfills the promise with the number of milliseconds as argument.

function timeout(durationMs) {
	var p = _.promise();
	setTimeout(function() { p.fire(true, [durationMs]); }, durationMs);
	return p;
}
Call it like this:
timeout(1000).then(function(ms) { window.alert(ms+ ' milliseconds have passed.'); });

Limited Promises Implementation in Web module

If you use only the Web module, instead of the full implementation, the promises implementation is not fully Promises/A+ compliant. One major difference is that it does not allow you create promises yourself. The only way to get a promise in the Web module is from functions like animate() and request(). The other difference is that the interoperability with other promises frameworks is limited, even though it should be good enough most of the time.

There are two things you may run into when you use Web's simplified implementation with a complete implementation:

  1. The simplified implementation does not support recursive thenables. So when you register callbacks with then(), you can return a promise or a thenable, but only if that promise is not also returning a promise.
  2. Many corner cases required by the Promises/A+ specification are not handled. When interoperating using reasonable implementations, you may never run into this, but Promises/A+ has detailed rules for things like then() methods implemented as dynamic getter and returning a new value on each invocation or throwing exceptions. If you need a water-proof implementation, you need to use the complete implementation in Minified's full package.

Comments

comments powered by Disqus

back to Overview

Functions