A promise in real life is something that you say you'll do at some time in the future.
i promise I'll pay you back for that lunch!
You'll pay them back at some time in the future.
The amount of time this takes will depend on how reliable you are but at the end of the day eventually you'll have to do the work of asking for the bsb and account details, going to your phone bank app and sending across the bank transfer.
The final payload of this process is when your friend receives the money a few days later when they check their bank account 💰.
i sent the funds across!
Or maybe you later realise that you don't have any money in your bank account to pay your friend back 🤪. The promise has to be broken!
You'll have to send your friend a message saying:
sorry I have no money 🤷♂️
You'll hardly ever have to write your own javascript promises but you'll be using them all the time with your own APIs.
Think of this concept in terms of dealing with the Pokemon API we played with a while ago in ruby.
We sent up a request to the API to get the Pokemon data; the Pokemon data then came back to us successfully. We didn't know how long the data was going to take to come back to us; theoretically it could have been a giant amount of Pokemon data we were after that takes around 3 seconds to come back or it could have been a single Pokemon object which takes around half a second to return.
This is the revolved value (the success value).
But... what if the server had been down! What if there had been some kind of error in the Pokemon API! We'd get an error message of some kind.
This is the rejected value (the error value).
A promise is an object that produces a single value some time in the future.
This is either a resolved value (successfully receiving the data payload) or a rejected value (an error message).
You might of heard of this word AJAX before.
AJAX stands for Asynchronous JavaScript and XML; it's a way we can use javascript to request data from an external source like an API. Since we are usually relying on another server physically located somewhere else in the world, we need to do this asynchronously.
This is an AJAX request in action in javascript.
const pokePromise = fetch("https://pokeapi.co/api/v2/pokemon/?limit=150")
console.log(pokePromise)
To make an AJAX request in javascript we use the fetch
function. This takes a string parameter containing the URL of the server we want to fetch data from and returns a promise.
fetch
is a Web API (we'll talk more about this next week) which means it's not included in node. We need to install a package to make fetch
work in node.
$ npm i node-fetch
Our code from before will now look this this.
const fetch = require('node-fetch');
const pokePromise = fetch("https://pokeapi.co/api/v2/pokemon/?limit=150")
console.log(pokePromise)
Let's talk about what is being console.logged right now.
We can see a promise object that says pending. The promise will always log pending as long as its results are not resolved yet. Regardless of the promise state (resolved or still pending) you must call another method .then
on the promise to capture the results.
const pokePromise = fetch("https://pokeapi.co/api/v2/pokemon/?limit=150")
pokePromise
.then((response) => {
console.log(response)
})
There's a lot of new concepts coming together here. Let's look at it.
In it's most basic terms this code can be read as:
fetch data from this url and then do this with it
To use the response data we need to pass a callback function to the promise's then
method, which the promise executes when it receives a response from the server. The response is passed into the function as a parameter, providing access for us to actually use it...once it exists.
But we still don't have the data... yet 🤪.
We have a response body and we need to pull the JSON data out it. To do that we can use the .json()
method. This returns another promise which we can then use a .then
on again.
const pokePromise = fetch("https://pokeapi.co/api/v2/pokemon/?limit=150")
pokePromise
.then((response) => {
return response.json()
})
.then((data) => {
console.log(data)
})
Boom we have some pokemon 🎉!
For a deeper explanation about promises check out this article.
When we make an AJAX request we get back a promise which is asynchronous meaning it does not wait for the response to come back from our API before executing the next line in the file.
This may seem confusing at first but javascript operates on a single thread, meaning it can only execute one command at a time.
Therefore, we need to make sure that commands that rely on external processes - such as our API - are not blocking, or do not hold up other commands.
Blocking is explained really well in this post on the node.js website.
So we can say that promises are a key design choice that allows for higher throughput and more performant code.
Later on we'll cover:
- How to handle rejected promises
- How to write our own promise objects