Promises are the new way™ to express "Do this, and once you're done, do that". In contrast to callbacks, promises are chainable.
From the readme of
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 no drop-in replacement for callbacks. Both have situations they shine in, with different implications and drawbacks. As a rule of thumb,
- use callbacks when you need to delegate control, e.g. in an event handler: "Hey, whenever a user clicks you, please run this code" (keyword whenever)
- use promises when you want to make time consuming things (like HTTP requests or animations) async, without having to write async code (keyword when you're done)
Many libraries return promises e.g. for async HTML requests. It may look like this:
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".
storePages = -> ... setupLinks = -> ... api.get('pages').then(storePages).then(setupLinks).then(...)
In case a promise could not be fulfilled, a second parameter will be invoked as error handler:
storePages = (pages) -> # Store them handleError = (error) -> # Notify user api.get('pages').then(storePages, handleError)
Building promises yourself
When you are building something with a lengthy execution time, you might want to make it async and offer its users promises instead.
You will encounter various implementations of promises. They all do the same thing in the end, but the way you construct a new promise differs.
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
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.
$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
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.
Promises are built into modern browsers and Microsoft Edge, but Internet Explorer does not support it in either versions.
Usage is similar to Angular's
promise = new Promise(resolve, reject) -> callAsyncFunction (result) -> if result == 'success' resolve() else reject() promise.then(-> alert('Promise was resolved!'))
You can experiment with callbacks vs. promises in our curriculum lesson.