Promises allow us to perform asynchronous operations. A Promise
is a proxy, a placeholder if you will, for a value that is not necessarily known when we declare the promise. Instead of immediately having the final value, we have a promise that a final value will be there.
They are useful when we need to do things such as store or retrieve data from a database or get data from an API.
How to create a Promise
To create a promise we simply need to make a new instance of the object and pass a function as a parameter with the resolve
and reject
parameters.
const promise = new Promise((resolve, reject) => /* do things */)
resolve
will be called if the asynchronous action completes successfully and reject
will be called if it doesn't. A promise can have three different states:
-
pending
is its initial state, it means that it hasn't completed yet -
fulfilled
means that the operation has resolved or completed successfully -
rejected
means that the operation has failed
So when the promise is first created, its state will be pending
. Then, once the asynchronous operation has taken place, if it resolved successfully its state will become fulfilled
and it will call the function resolve
. Otherwise, it will be rejected
and call the function reject
.
So a quick example of a promise could look like this:
const promise = new Promise((resolve, reject) => {
console.log('Asynchronous operation started')
setTimeout(() => Math.random() > 0.15
? resolve('Success!')
: reject('Oops, something went wrong!')
, Math.random() * 700 + 800)
})
The first thing that we'll get here is a message in our console letting us know that the operation has started. Then, after 0.8 to 1.5 seconds, the promise will either resolve (~85% of the time) and return a success message or fail (~15% chance) and return a failure message.
then
and catch
or what happens when the promise resolves
Most often, after the asynchronous operation has resolved, we'll want to do something with the returned data. If, for example, we are retrieving information from a database, we might want to actually use that information. That's where the methods then
and catch
come in handy.
then
The method then
accepts two optional parameters, onFulfilled
and onRejected
. The first one will be called if the promise is fulfilled
and the second one if it is rejected
. Both functions will get one argument, which is the value returned by the promise.
Building on our previous promise, it could look something like this:
promise.then(data => {
writeMsg(data) // Writes 'Success!'
launchFireworks() // Launches fireworks
}, rejection => {
writeMsg(rejection) // Writes 'Oops, something went wrong!'
playDefeatMusic() // Plays sad, defeat music
})
Often, though, you'll just want to pass the onFulfilled
parameter and leave the logic that deals with rejection for a catch
method. So you we could just write this:
promise.then(data => {
writeMsg(data)
launchFireworks()
})
If you only need to pass one function to the then
, you can just pass its name and the then
will take care of calling it and passing the result of the promise as the function's argument.
//Both these thens do the same
promise.then(data => doStuff(data))
promise.then(doStuff)
catch
The method catch
accepts the parameter onRejected
, which will be called if the promise rejects. Other than that, it works exactly as then
.
promise
.then(data => {
writeMsg(data)
launchFireworks()
})
.catch(error => {
writeMsg(error)
playDefeatMusic()
})
And just like then
, you can use shorthand when calling it:
promise
.then(doStuff)
.catch(logError)
Chaining then
and catch
Whatever is returned by then
and catch
will also be wrapped in a promise. So it is possible to chain them even if they are not really doing asynchronous stuff.
promise
.then(transformData)
.then(doMoreAsyncStuff)
.then(transformData)
.catch(dealWithError)
But maybe it's time we looked at a real example, instead of something filled with mock functions. Let's suppose that we are using MongoDB to store data about our exercise sessions. At some point, we want to retrieve said data. So we could do something like this:
const mongoDB = require('mongodb')
mongoDB.MongoClient.connect(URI)
.then(client => client.db('exercise'))
.then(db => db.collection('workouts').find(query))
.then(data => data.toArray())
.then(console.log)
.catch(console.warn)
This creates a connection to our MongoClient, which already returns a promise by itself. Then it selects the database exercise
. Then it selects the collection workouts
and looks for something that matches the criteria specified in query
. Then it transforms the returned data into an array. Then, if everything has gone well, it logs the data into our console. If anything goes awry in the process, it will log it as a warning in the console.
Making a function that returns a promise
If we use MongoDB, fetch
or any function that returns a promise, we can just chain then
and catch
methods to it and that's all that we need to do to work with promises. But this isn't always the case. Sometimes, we might need to create a function that returns a promise first.
For example, let's imagine that for our exercise database we decided to use some database whose API for JavaScript doesn't return promises. Instead, it takes callbacks to deal with the returned data. So we would have to do something like DbHandler.find(query, callback)
when we want to do something with retrieved information. And let's imagine that the callback should take two parameters data
and error
, which will be the retrieved data and the errors that might have happened.
Then we can create a function that looks up stuff in the database and returns it as a promise:
const findPromise = query => new Promise((resolve, reject) => {
DbHandler.find(query, (data, error) => {
if (error == null) return resolve(data)
else return reject(error)
}
})
And now when we want to look up stuff in our database, we can call our crafted function like any other function that returns a promise:
findPromise(query)
.then(doStuff)
.catch(console.warn)
Top comments (4)
Nice article, just a small comment.
In javascript, a function that doesn't return anything technically returns
undefined
, you can still chain those functions in a promise chain, it's just that the resolved value will beundefined
.Good catch! I guess I should reword that.
Thanks^
This is an awesome tutorial on promises. Clean, quick and useful. 😁👍
Thanks a lot for the feedback!