JavaScript Promises Tutorial: how to write asynchronous code
Amanda Fawcett
Posted on June 16, 2020
This article was written by Debbie Otuagomah and was originally published at Educative, Inc.
If you are currently learning to program with JavaScript or already use it, you have probably seen the words "promises" or "async" floating around. These are features of JavaScript that allow you to set up asynchronous operations in your code. Asynchronous programming allows you to do multiple things at once in your code. Today, I will introduce you to asynchronous JavaScript and show you how to use promises, a feature of JavaScript that alleviates the limitations of callback functions. We will cover:
- What is asynchronous JavaScript?
- Why not to write synchronous code
- What are JavaScript promises
- How to change synchronous code to asynchronous code
- Wrapping up and resources
What is asynchronous JavaScript?
At its root, JavaScript code is synchronous, meaning that only one operation can be in progress at a given time. However, this becomes a problem when, for example, you need to make huge data requests. You have to wait for eons for your database management system to handle the request properly. Enter the solution: asynchronous code.
When you click on a website that uses asynchronous code, data is called from a server, and the HTML, CSS, and JavaScript on that website automatically updates. Asynchronous JavaScript allows us to get data from the server without reloading the entire page.
For instance, when you search on Google for a particular topic, you can see that the search engine automatically completes what you are looking for, even without an extra click. What happens under the hood is that the website is using AJAX, sending some requests to the server and getting you the data you see on the fly in a format called JSON. It then parses the JSON code and updates the website as needed.
Note: AJAX (Asynchronous JavaScript and XML) is a combination of:
- A built-in XML HTTP request object used to request data from a web server
- JavaScript and the HTML DOM used to display or use the requested data
Comparing synchronous and asynchronous code
The code snippet below contains a piece of synchronous code. Here, JavaScript will read our code from top to bottom and execute one line at a time. It will check our getTestScore
function, add it to the memory, and call console.log
the word Start
below it. When we call a function in JavaScript, we create a new context for execution, and our machine does what the function says. After that, we can console.log
out End
, following our top-to-bottom approach.
On the other hand, asynchronous code does not follow this pattern: it allows you to continue programming while your code runs other operations. You don’t have to wait until the ongoing operation ends. A good example here can be shown using the setTime
function. This is a native JavaScript function that is used to set a timer. It executes a callback function once the timer runs out. Let’s see a quick example below.
In this piece of code, we use the setTimeout
function to delay the machine from logging that message by two seconds. So, when we run this code, we will get the Start
message and wait only two seconds for the status message. Then, we get End
, which, as you can guess, probably executed before the callback function.
A quick thing to note here is that when we execute JavaScript in the browser, functions like setTimeout
are handled by web APIs that take care of the delay and pass the function back to the browser when it’s done, so we can run the callback function. Another example of what is passed to these APIs is a click function, which keeps track of when the user clicks so the callback function can run.
What is wrong with writing synchronous code?
Since synchronous code works fine, what then, is wrong with writing it? Well, the problem is that with synchronous code, only the block of code you just wrote or currently have will work. Any other contrasting piece of code will not run. Like I mentioned before, JavaScript is a single-threaded language and can only execute the things you tell it, one operation at a time.
A few weeks ago, I coded a quick homepage for a Q&A website for a web development project and wrote some code to show a user the signup form if they clicked a button. However, the piece of code I added to let the user close the form did not run until I added a callback function, which in this case was asynchronous.
This is the issue with synchronous code. Because JavaScript runs one thing at a time, any other useful piece of code might prove cumbersome to run and will be blocked until the previous piece(s) of code finishes running. Let’s see an example below:
In this code snippet, we can see some code that will take in a user’s email and password and return the email from the server after two seconds. The variable, newUser
is used here to call the function with a new user’s email and password, which we have also passed. Now, what happens here is that the setTimeout
function actually returns the email after two seconds, but if we try to console log the value of the newUser
variable, it won’t run. If we check this in the browser, we will get an error, undefined
.
Why? Did we not do everything right? We did. Synchronous code is just doing its thing, and the information in the setTimeout
function did not return what it was supposed to when we tried to run our function on a new variable.
However, if we change the seconds to 5000 and actually wait five seconds as we did above, we will get the message, The data is here
.This is because, with synchronous code, JavaScript has to finish running everything we tell it while we wait. It does not allow us to do something else during that period.
How do we fix this? We write asynchronous code instead to save us all the trouble of waiting. One of the ways asynchronous code is run is through the use of JavaScript promises.
What are JavaScript promises?
In JavaScript, callback functions were initially used for asynchronous operations. However, callbacks were limited, so promises were introduced as a solution. In real life, you can make a promise and pledge to do something at a particular time. In modern JavaScript, a promise here is similar.
A promise represents an operation that has not yet completed. It is an object that gives us the result of a failed or successful asynchronous operation. Think of this as an object that says, "Look, whether your operation fails or succeeds, I will let you know."
A promise object has one of three states:
- pending: is the initial state.
- fulfilled: indicates that the promised operation was successful.
- rejected: indicates that the promised operation was unsuccessful.
Keep the learning going.
Learn modern JavaScript without scrubbing through videos or documentation. Educative's text-based courses are easy to skim and feature live coding environments - making learning quick and efficient.
The Complete Guide to Modern JavaScript
How to create a simple promise
This example code shows you how to create a simple promise in JavaScript. Run the code to see it in action. I will break this down step-by-step below.
let promise = new Promise((resolve, reject) => {
// Code to perform the promised task
let task_performed = true;
if(task_performed) {
resolve('The promised task was performed successfully.');
} else {
reject('The promised task was not performed.');
}
});
promise.then((fromRes) => console.log(fromRes)).
catch((fromRej) => console.log(fromRej));
A
promise
is created using a constructor that takes a call back function with two arguments (line 1).The code needed to perform the promised task is written. In this example, it is assumed that the code executes successfully (line 2).
If the task is successful, the promise is resolved. In this example, the optional parameter “The promised task was performed successfully” is passed (lines 4-5).
If the task is unsuccessful, then the promise is rejected. In this example, an optional parameter is passed (lines 6-7).
The
then()
method is called when the promise is resolved, and thecatch()
method is called if the promise is rejected or if there was an error during the code execution (lines 10-11).
You can also use the
Promise.all()
method to return a single promise that resolves when all of the passed-in promises have resolved.You can use
Promise.race()
to return a promise that resolves or rejects when one of the promises resolves or rejects.
Promises in action: continuing our example
In our case, we want to use this promise to fix the data issue we had in our code from before. So, we need a promise to tell us whether we get our data or not. Let’s look at an example below.
Here, we have created a new promise object with a constructor that takes in two parameters called resolve
function and reject
function. The first one is for when our asynchronous operation is successful, while the second is for when it fails. After that, we will add our setTimeout
function and pass in a user to the resolve parameter. However, we still have to execute this promise to get the information we want.
In the piece of code above, we called the promise and appended a method called then
to give us the result of the previous code. Now, if you run this piece of code, we will get the user returned after two seconds. Easy peasy, no?
What if we got an error and pulled no data? We can look at how that will work by replacing the resolve in our promise to reject using promise rejections. To see what happens when the code does not resolve but gets rejected, let’s look at the piece of code below:
Here, we used reject
instead and passed a new error object that tells us that the username is not found after we try to pull data from the server.
To handle this new situation, we use a concept called promise chaining. In JavaScript, we can do something called method chaining, where we call multiple methods on one particular object. It helps avoid repetition and allows us to carry out different operations on an object without breaking the flow of the code we have written. With promises, it will look like this:
In this piece of code, we have chained a method called catch
to our already-existing code to log an error in case the username is not found. Notice here how we did not need to start writing another separate block of code or a new function. We just added a new method and our code runs fine.
Therefore, if we try to pull a user’s name and it’s successful, the first part of the code will run, but if we get an error and can’t access the data we want, the code we just added through chaining will run instead, just like an if/else statement.
How to change synchronous code to asynchronous code
Remember the piece of code we wrote when we were talking about how synchronous code can create problems if we want to run different pieces of code at the same time? The one with the user login function? Since we know how to use promises in JavaScript now, let’s see how to make the code look and run better. For a quick refresher, our old synchronous code looked like this:
To refactor this piece of code and make it asynchronous, we will add a new promise like so:
Here, we added a new promise object and put the setTimeout
function inside of it. Our code looks cleaner now,will run smoothly, and will show what we want after just two seconds. As usual, if we get the data we requested, we will pass resolve
next to ‘{usermail: email}
in place of return
, and we’re good to go. We can also call reject
and chain a catch method that will tell us our data request has been rejected.
How then do we successfully call our userLogin
function and pass new parameters? Before we look at that, we have to note that in practice, you may not even have to write the preceding code from above. Most times, when you request data from a web API, it automatically returns a promise. We have just written it all out to see how it works under the hood.
If you remember, when we tried to test our function and console log the newUser
variable, we got undefined
. To call our function now with new parameters, we will add a then
method like so:
Here, we are executing the userLogin
function with a new email and password. We have also attached a then
method that gives us access to this new user. This piece of code will return this new user’s data, just like we asked it. Compared to what our code looked like before, this is easier to read, cleaner, and more effective. Knowing how to write asynchronous code is important as a developer. It helps you run different pieces of code separately from your main application.
Wrapping up and resources
I hope this article has helped you understand how you use JavaScript promises to run asynchronous code. Some of the topics you should learn to master asynchronous programming in JavaScript are:
-
async
&await
var
Promise.all()
Promise.race()
- and more
This small change to your JavaScript code will save you time, energy, and make your code cleaner overall. It’s a powerful tool necessary for being a modern JavaScript programmer. So, why wait to learn JavaScript promises and asynchronous functions? To get you started, here are some essential resources for learning promises and asynchronous JavaScript.
Posted on June 16, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 6, 2024