Promises are the new way™ to express "Do this, and once you're done, do that". In contrast to callbacks, promises are easily chainable. From the readme of
Q
Show archive.org snapshot
, an early implementer of the pattern:
The callback approach is called an “inversion of control”. A function that accepts a callback instead of a return value is saying, “Don’t call me, I’ll call you.”. Promises un-invert the inversion, cleanly separating the input arguments from control flow arguments. This simplifies the use and creation of APIs, particularly variadic, REST and spread arguments.
Note that promises are not a drop-in replacement for callbacks. Both have situations they shine in, with different implications and drawbacks. As a rule of thumb,
- use callbacks to delegate control, e.g. in an event handler: "Hey, whenever a user clicks you, please run this code" (keyword "whenever")
- use promises to make time consuming things async without writing async code, e.g. for animations: "Hey, once you're done with that animation, please run this code (keyword "once")
Promises are built into modern browsers and Microsoft Edge, there are polyfills for Internet Explorer Show archive.org snapshot . Usage:
Usage
Assume this piece of code returns a promise:
api.get('pages') // => Promise
You may now chain function calls to the promise using .then()
, without needing to wait for the HTML request to actually return. The promise will collect all functions passed to then()
and invoke them once the original promise "resolves".
const storePages = function(pages) {};
const notifyAboutError = function(errorMessage) {};
api.get('pages')
.then(storePages, notifyAboutError
.then(setupLinks);
In case a promise could not be fulfilled, the second parameter of then
will be invoked as error handler.
Wrapping a callback in a promise
let p = new Promise(resolve, reject);
let promise = p(() => asyncFunctionWithCallback(function(result) {
if (result === 'success') {
resolve();
} else {
reject();
}
}));
promise.then(() => alert('Promise was resolved!'));
You can experiment with callbacks vs. promises in our curriculum lesson.
Legacy information
jQuery
With jQuery, you would do it like this:
deferred = $.Deferred()
callAsyncFunction (result) ->
if result == 'success'
deferred.resolve()
else
deferred.reject()
promise = deferred.promise()
promise.then(-> alert('Promise was resolved!'))
In jQuery, the difference between a "Deferred" and a "Promise" is that a deferred has resolve
and reject
methods in addition to then
. A Promise only has then
. When you hand back your promise to other code, it's considered good practice to only give back a Promise, never a Deferred.
AngularJS 1.x
In Angular's $q
promise library, you would do it like this:
promise = $q (resolve, reject) ->
callAsyncFunction (result) ->
if result == 'success'
resolve()
else
reject()
promise.then(-> alert('Promise was resolved!'))
$q
calls its argument function with two functions resolve
and reject
. These are used to propagate the outcome of the promise. Call resolve()
to fulfill the promise, or reject()
if something went wrong.
Inside the argument function you can do whatever you like. Just make sure to call the respective function when you're done.