• 10 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 4/2/20

Understand asynchronous programming

Log in or subscribe for free to enjoy all this course has to offer!

What does asynchronous mean, exactly? Why does it matter? What does it mean for JavaScript? How do I "use" asynchronous programming? I hope to answer all of these questions in this chapter. The theory may seem a bit dry and daunting, but I Promise (you might not get that joke yet, but you will!) that once you've got the gist of asynchronicity, you will have a much deeper mastery of JavaScript, especially with AJAX.

JavaScript is synchronous and single-threaded

…wait, what? 🤔

That means that your JavaScript code runs line by line, each line needing to be completed before the next one starts (in what is called the event loop).  This means that while one piece of JavaScript code is running on a web page, for example, no other piece can be running at the same time.

Take this example:

console.log('Calculating 500th Fibonacci number...');
let a = 1;
let b = 1;
let temp = 1;
for (let i = 0; i <= 500; i++) {
temp = b;
b = a + b;
a = temp;
}
console.log('Answer is: ' + b);

What happens here is:

  • the first console log is shown

  • each variable is initialized in turn

  • the  for  loop runs until completion, setting the 500th number in the Fibonacci sequence to  b

  • the second console log is shown

This seems to make a lot of sense, right? One line completes, the next line begins, etc.

But, as you will have figured out by now, not everything in JavaScript is synchronous.

Sometimes, things take time

Think back to our AJAX requests from the previous part of the course.  To send a request, we did:

apiRequest.send();

All that line does is send our request. Our script will begin the sending of the request and then move straight on to the next line. It cannot take into account how long that request may take to complete. There may even be server issues which mean that we never receive a response. What this means is that we cannot have a line immediately after it which relies on the request having finished.

Therefore, our request is asynchronous, i.e. our code will continue executing whether or not the request is complete.

Let's try out an example of this.

Practice!

Head to CodePen Exercise P3CH1a and follow the instructions below.

We're going to use the setTimeout() method to simulate a server taking time to approve our request. This should make obvious the inherent difficulties in dealing with asynchronous code (don't worry, we'll also look at several solutions!).

  1. Start by adding a variable called approval, with value 'Not approved!'.

  2. Now create a function called getApproval() which will, after 500ms, set the value of approval to 'Approved!'.

  3. Add a line calling the getApproval() function, and add a line immediately afterwards which sets the text content of the result heading to the value stored in approval.

Reading this code, what would you expect to happen? What actually happens? Is it what you expected?

Once you've given it a go, watch me code a solution and see how your approach compares:

How do we solve the issue of having code that takes time? In other words, how do we deal with asynchronous code?

Callbacks

The old school solution is the callback function. This is a function passed as an argument to an asynchronous function, and is called once all the asynchronous stuff is out of the way. Let's set up a callback in our example to see how it works.

Practice!

Head to CodePen Exercise P3CH1b and follow the instructions below.

  1. To add a callback to our getApproval() function, we need to be able to pass a function as an argument. Change the function declaration function getApproval() to function getApproval(callback).

  2. Now let's add a call to the callback function inside getApproval(). We want it to be called once our request is 'Approved!', so add a function call to callback() just after we modify approval.

  3. Now we want to leverage our now asynchronous getApproval() function to set our result's text content once our request is approved. Remember, what we put in the callback is what we want to happen once the asynchronous stuff is complete. Therefore, we are going to call getApproval() and pass it an arrow function that sets our text content.  Modify our getApproval() call to:

    getApproval( () => { result.textContent = approval; } );

    Was your request successfully approved? 

Once you've given it a go, watch me code a solution and see how your approach compares:

As I mentioned above, callback functions are an older way of doing things. In fact, they're often baked into JavaScript.

For example, when we set the  onreadystatechange  function for an  XMLHttpRequest , what we are in fact doing is declaring a callback that is executed every time the  readyState  property changes. Equally, the function you set as an argument to  setTimeout()  is also a callback, as it is executed after X milliseconds.

However, using callbacks can quickly lead to a place affectionately known as "callback hell," with lots of nested and hard-to-read code (there is a website called callbackhell.com which neatly describes the concept, if you are interested). Therefore, we were all very happy when we learned about the invention of promises.

Promises

Promises can be quite tricky to understand (or they were for me to begin with), so let's take things step by step.

Let's say we want to retrieve someone's name from a server. Once we have retrieved it, we want to build a string, something like  'Hi ' + name + ', how are you today?' .  That request will take some time (it is asynchronous), so we can only use  name  once we are sure we have it.  What if we could promise that  name  will exist?

To use promises in our code, we get our asynchronous function to return a promise which will  resolve  with the retrieved data once it has been received. It can also  reject  if an error occurs. Then, when we call our asynchronous function, we can use  .then()  to run code after the promise has resolved (with the received data) and  .catch()  to handle any errors that may occur.

In theory, this all seems a bit messy, so let's apply a promise to our previous example to see how it all comes together.

Practice!

Head to CodePen Exercise P3CH1c and follow the instructions below.

As we saw in the theoretical discussion of this chapter, an asynchronous function working with Promises needs to return a Promise. Let's see how that works.

  1. Empty out our getApproval() function and, for now, simply replace the contents with:

    return new Promise();.

  2. The constructor for a Promise takes a function as an argument. That function takes two arguments: resolve and reject. Pass an arrow function to our Promise constructor:

    return new Promise((resolve, reject) => { });
  3. Now instead of modifying the variable directly within the getApproval() function, we're going to have its Promise resolve with 'Approved!' after 500ms. Rebuild a setTimeout() for 500ms, within whose callback you will write:  resolve('Approved!'); . So when we run getApproval(), it returns us a Promise which resolves after 500ms.

  4. We now need to use a .then() block to use the data resolved within that Promise. Let's modify our call to getApproval() and how we set result.textContent as follows:

    getApproval().then( (resolvedApproval) => { result.textContent = resolvedApproval; } );

    The function we pass to our .then() block receives the data we pass to our resolve() function, and the whole thing works!

Once you've given it a go, watch me code a solution and see how your approach compares:

Promises already make things much tidier than callbacks. However, we can get into more messy code when things start to nest (although there are methods for cleaning these up a little). Therefore, there's a much newer way to code asynchronously.

Async/await

No two words have made JavaScript developers smile as much as  async  and  await . These two little words allow us to suspend execution of our code while we wait for asynchronous code to finish.

Async/await still uses promises, but allows us to handle them in a more readable and easier to maintain manner. Let's try this out with our example.

Practice!

Head to CodePen Exercise P3CH1d and follow the instructions below.

Seeing as async/await is a different way of handling Promises, we don't have to make any modifications to our getApproval() function. What we need to change is how we call that function and handle the data from it.

  1. Completely remove our current call to getApproval().

  2. We are now going to create a function that uses async/await to set our approval text. Declare a new asynchronous function called setApprovalText() as follows:  async function setApprovalText() { } .

  3. We need the async keyword to be able to use await within the function body to wait for our Promise to resolve. Within the new async function, declare a constant called approvalPromise which holds the Promise returned by getApproval():

    const approvalPromise = getApproval();.

  4. Now comes the cool part: we are going to use the await keyword to assign the eventual resolved value from the promise to our result element's text content. Add the following line to the async function:result.textContent = await approvalPromise;.

  5. Now all we need to do is call the function. Call setApprovalText().

Once you've given it a go, watch me code a solution and see how your approach compares:

While async/await does not gain us much in this very simple example, you will soon see how it makes potentially complex code far easier to read and, therefore, to maintain.

Let's recap!

In this chapter, we covered the concept of asynchronous code: what it is, and how we handle it using three main techniques:

  • callbacks: the most old-fashioned technique, but still an essential one to recognize and understand,

  • promises: possibly the most widely used technique today, they can seem complicated but are absolute life savers,

  • async/await: an easier-to-read way to handle promise manipulation.

In the next chapter, we will explore a new kind of asynchronous HTTP request.

Example of certificate of achievement
Example of certificate of achievement