Promises
What are promises
A promise is an object that represents an async operation (process) that’s still in progress. The purpose of Promises is to make it easier to write sequential code.
Promise.try(() => {
return taskOne();
}).then((result) => {
return taskTwo();
}).then((result) => {
return taskThree();
});
In the above snippet, even though there are technically 6 promises involved, only three implement real functionality:
- Promise.try(…) -> Tracks the result of taskOne below
- taskOne -> Implements real functionality
- .then(…) -> Propagate the
result
- taskTwo -> Implements real functionality
- .then(…) -> Propagate the
result
- taskThree -> Implements real functionality
The order of execution:
--- tick 0 ---
Promise.try called
taskOne called
a Promise is returned from taskOne
return that Promise from the Promise.try callback, Promise.try will now track it
first .then called (note, .then itself, *not* the callback) with a callback as argument
second .then called, with a callback as argument
--- tick 10 ---
taskOne Promise resolves
Promise.try Promise resolves
first .then callback called
taskTwo called
a Promise is returned from taskTwo
return that Promise from the first .then callback, the first .then will now track it
--- tick 20 ---
taskTwo Promise resolves
first .then Promise resolves
second .then callback called
taskThree called
a Promise is returned from taskThree
return that Promise from the second .then callback, the second .then will now track it
--- tick 30 ---
taskThree Promise resolves
second .then Promise resolves
--- the end ---
What’s the difference with callbacks?
The main difference between Promises and callbacks is that a Promise is a placeholder object you get access to immediately that represents the in-progress operation; in a callback you have to immediately specify the next behaviour.
Nested callbacks also allow for writing sequential async code, but are more difficult to use right. Similar functionality of the above snippet as a series of nested callbacks:
taskOne((err, result) => {
taskTwo((err, result) => {
taskThree((err, result) => {
// ...
});
});
});
With error-first callbacks you have to immediately specify the next code to run when a task completes. This is not true for Promises. With Promises you are able to attach behaviour at any time including after the Promise has already resolved. This allows for a lot of improvements:
- Compose things easier -
Promise.all([promise1, promise2])
will produce a combined Promise that resolves whenpromise1
andpromise2
both resolve.Promise.all
wires up things internally, which it can do because it can specify behaviour at any time onpromise1
andpromise2
, including its own behaviour. - Propagate errors automatically - Because behaviour can be attached at any time, you can return a Promise from another Promise’s
.then
callback, and then that other Promise can wire up error propagation automatically. - The returned value isn’t the actual result, but it can be returned and passed around just as easily.