Blog
It's a Wonderful Life
ES6 promises
How does a Promise work?
The Promise objects represents the eventual completion of an asynchronous operation. It returns a single value based on an operation being resolved or rejected. A promise is always in one of three stages:
- fulfilled
- rejected
- pending
The Promise() constructor takes two arguments: a resolve
function and a reject
function. It returns one or the other based on the outcome of the asynchronous operation.
Creating a Promise
You can create a promise in using the Promise constructor:
const myPromise = amount => {
return new Promise((resolve,reject) => {
if(amount > 0){
resolve("success!")
}
reject("failure!")
})
}
myPromise(1)
//resolves Promise { 'success!' }
In the example above, we create a myPromise()
function that takes a single amount parameter and returns a Promise object. The promise constructor function takes two arguments: resolve
and reject
.
We then call our myPromise(1)
function. Notice how it returns a resolved Promise object with the “success!” message since our argument 1
is greater than 0
.
If we hadn’t wrapped our Promise in a function, then it would have executed when it was defined rather than when we invoked it via myPromise(1)
.
Promise Methods
Promise methods exist to handle the resolution or rejection of a Promise object. Below is a brief description and example of the methods commonly used with promises:
then()
The then()
method executes after a promise is either fulfilled or rejected. It takes two function arguments for resolved
and rejected
.
let handleSuccess = (x) => {
console.log(x + " it worked!")
}
let handleError = (x) => {
console.log(x + " oh no, it failed!")
}
const myPromise = amount => {
return new Promise((resolve,reject) => {
if(amount > 0){
resolve("success!")
}
reject("failure!")
})
}
myPromise(1).then(handleSuccess, handleError)
//logs 'success! it worked!'
myPromise(0).then(handleSuccess,handleError)
//logs 'failure! oh no, it failed!'
Notice how we call then()
twice on our constructed myPromise()
function. It’s important to remember that the method accepts two functions as arguments, in our case handleSuccess()
and handleError()
. Notice how the original message gets passed to the handler function for both resolved
and rejected
scenarios.
catch()
The catch()
method provides a better way to handle rejections and failures. It is a “catch all” for any rejected promise:
const myPromise = amount => {
return new Promise((resolve,reject) => {
if(amount > 0){
resolve("success!")
}
reject("failure!")
})
}
myPromise(0).then(res => {
console.log(res + " success!")
}).catch(err => {
console.log(err + "oh no, it failed!")
})
//logs 'failure! oh no, it failed!'
In the above example, catch()
takes the returned promise from the then()
function and handles the rejection with its own argument function. Notice how we only pass a single argument to the then()
function for handling a successful response.
Not only does the catch()
method save us from having to specify the second argument for then()
, it also catches any other errors along the way. Any internal errors thrown by then()
will still be caught by catch()
:
const myPromise = amount => {
return new Promise((resolve,reject) => {
if(amount > 0){
resolve("success!")
}
reject("failure!")
})
}
myPromise(1).then(res => {
throw new Error();
console.log(res + " success!")
}).catch(err => {
console.log(err + "oh no, it failed!")
})
//logs 'Error oh no, it failed!'
Even if we throw an error within our then()
handler, the catch()
method will still catch it.
Promise.resolve()
Returns a resolved promise with the given value:
Promise.resolve("Success")
//returns Promise {'Success'}
Promise.reject()
Returns a rejected promise with the given value:
Promise.reject("error")
//returns unhandled promise rejection
Promise.all()
The all()
method takes an array of promises as an argument. It returns a resolved
promise based on the referenced promises ALL being fulfilled
or rejected
:
const p1 = new Promise((resolve,reject) => {
setTimeout(resolve("p1 success"),2000)
})
const p2= new Promise((resolve,reject) =>{
setTimeout(resolve("p2 success"),4000)
})
Promise.all([p1,p2]).then(res => {
console.log(res);
})
//logs ['p1 success', 'p2 success'] after 4 seconds
Promise.race()
The race()
method takes an array of promises as an arugment. It returns a resolved
promise based on the first referenced promise that resolves.
const p1 = new Promise((resolve,reject) => {
setTimeout(resolve("p1 success"),2000)
})
const p2= new Promise((resolve,reject) =>{
setTimeout(resolve("p2 success"),4000)
})
Promise.race([p1,p2]).then(res => {
console.log(res);
})
//logs 'p1 success' since p1 finishes first
Since p1
is the first to resolve, race()
returns the resolved
promise for p1
.
ES6 Promise Chaining
You’ll notice we call then()
and catch()
with dot notation. This is called chaining
and it works because we return a resolved
promise for each chained method.
Chaining
can be a powerful tool. As long as you are returning a promise, you can extend async
operations indefinitely.