Hamburger Icon
  • Labs icon Lab
  • Core Tech
Labs

Guided: Asynchronous JS

In the JavaScript ecosystem, asynchronous programming is essential in order to provide a good experience to users of your app or website. Running code "in the background" or in parallel can help you perform needed tasks without blocking the user interface. In this Guided Lab, you will learn all about asynchronous programming in JavaScript as you gain practical experience using callbacks, JavaScript Promises and the async/await syntax.

Labs

Path Info

Level
Clock icon Intermediate
Duration
Clock icon 56m
Published
Clock icon May 29, 2024

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Table of Contents

  1. Challenge

    ### Introduction

    In this lab you are going to learn the ins and outs of async programming in JavaScript. To put it simply, async code runs in the background so that your main application code can continue running unblocked in the foreground. Async programming is a critical subject to understand when working with JavaScript since many JavaScript applications rely on user input. If you were to perform an operation like requesting data over the network in a synchronous fashion, then your entire application would be blocked until the request completed. This would render your application completely useless during that time!

    A major benefit to async programming is that it lets you perform long-running operations in the background so that your users are free to interact with your application during that time. Async programming in JavaScript can be implemented in several ways but the most important techniques include:

    • Callbacks/Event Handlers
    • The Promise API
    • Async/Await syntax

    This lab will guide you into a full understanding of async programming in JavaScript starting from the oldest async programming techniques to the most modern. First, you will gain an understanding of JavaScript event handlers and callbacks – the oldest and often the most complex way of writing async code.

    From there you will dive deep into the JavaScript Promise API – a groundbreaking API that has been the cornerstone of async programming in JavaScript for quite some time. Then you’ll build on your knowledge of the Promise API by learning about modern async/await syntax in JavaScript – a simple syntax that abstracts away a lot of the complexities of the Promise API.

    When you make changes to the JavaScript file, you can simply refresh the Web Browser tab to get your new code to take effect.

    info> Note: The solution directory contains the final solution. Feel free to view the file in that directory should you get stuck!

  2. Challenge

    ### Async Programming with Callbacks

    No learning journey into async JavaScript would be complete without a digression into callbacks. Callbacks are a specific type of function that are used for, you guessed it, “calling back”. To put it simply, callbacks are functions that are designed to be called when an event happens or some other form of processing is complete.

    Before more modern async programming APIs became available in event-driven environments like the browser, it was necessary to use callbacks in order to tell your programs what functions to call when certain events were triggered. To this day, there are many usages of callbacks in the DOM (Document Object Model) API and in JavaScript programs in general although more modern APIs like the Promise API along with async/await syntax are becoming increasingly prevalent.

    In this step, you are inheriting an application that is in desperate need of some async programming functionality. Throughout this portion of the lab, you are going to create and use callbacks to facilitate primitive, async programming. The most prevalent example of a callback in the JavaScript ecosystem is the event handler. Event handlers are functions that you assign to be called when certain events (in this case browser events) occur. Event handlers are an extremely common way of creating callbacks and thus writing asynchronous code in JavaScript but another example of callback usage has to do with the very popular and commonly used JavaScript function setTimeout. It is very common in JavaScript to use setTimeout to delay the execution of a function by a given number of milliseconds. The setTimeout function works by first receiving a callback which it will execute following the number of milliseconds given to it in the second parameter. Well done! You now have a good grasp of asynchronous code in JavaScript by way of callbacks. But, as you can see, callbacks are a little clunky. The misdirection that happens in the code with callbacks makes the code hard to follow – in fact, callbacks are a great recipe for “spaghetti” code in a large application. This is because you must jump back and forth to see where your callback functions are being executed - “following the spaghetti” so to speak. There are many APIs in JavaScript (like the DOM event API) that are set to use callbacks. But one of the most predominantly asynchronous portions of JavaScript code has to do with HTTP requests. In the next step, you’ll learn how to use Promises to write asynchronous code that is easier to reason about.

  3. Challenge

    ### The JavaScript Promise API

    In this step, you are going to build on your foundational knowledge of async programming in JavaScript by learning and using the JavaScript Promise API. In JavaScript, a Promise represents the eventual result of an asynchronous operation. A Promise can succeed or fail and the native Promise type provides an API which can be used to reason about these two states. Promises were introduced with the ES6 version of JavaScript so they’ve been around for a quite some time.

    Promises are seen as a step above callbacks in terms of readability and maintainability. You can create a Promise with the new keyword like this:

    const myPromise = new Promise((resolve, reject) => { 
       if (true) { 
         resolve(true); 
       } else { 
         reject(); 
       }
    }); 
    

    Note that to create a promise, you pass it a function that accepts two parameters - resolve and reject – both of which themselves are functions. The resolve function is used to return a value upon successful completion of the Promise. The reject function is used to signify an error state i.e. that the Promise failed. In the last task you created your first Promise, but how do you access the underlying value that the Promise will eventually resolve to? You can wait for your Promise to resolve and do something with the output with the then method that is available on Promises. You can also chain promises together using the then method. The then method helps you capture data from Promises when things go right... but what about when something goes wrong and your Promise cannot resolve? Well that’s when the Promise goes into a “rejected” state. To capture this error state, you must use the catch method. Like then, catch accepts a function where the first parameter is the value (often times an error) that is caught. Sometimes when working with Promises, you need to do some work irrespective of whether or not a Promise is resolved or rejected. To account for this, you can use the finally method on a Promise. LIke then and catch, finally also accepts a function. Working with a single Promise is pretty straightforward, but what about when you have multiple Promises that should be resolved together? This is what the static method on the Promise object all is used for. Promise.all can be called with an array of Promises – this function will execute each Promise it receives in parallel and resolve to an array of Promise responses in the same order as the original Promise array that was passed in. The Promise.all function returns a Promise that can be chained in the usual fashion. Another useful function that works with multiple Promises is the Promise.race function. This function also accepts an array of Promises but it will resolve whenever the first Promise that it executes resolves. This is helpful when you have multiple asynchronous operations that you need to execute but you want the fastest operation to win out. Well done! You’ve finished this step on working with Asynchronous JavaScript using the Promise API. Promises make reasoning about asynchronous code much easier then callbacks many times. But Promises still suffer from readability and maintainability issues – particularly when you have large Promise chains with very many calls to then and/or catch. There is a better way! In the next step, you’ll work with the JavaScript async/await syntax.

  4. Challenge

    ### A Cleaner Approach with Async/Await

    In the last step you learned about the ever-useful Promise API. As you may have noticed, code that uses or works with Promises can get a little verbose. But don’t worry, there is a way to use Promises in a very clean and succinct way. The key is the async/await syntax. The async and await keywords let you write asynchronous code in JavaScript in a way that reads synchronously. This makes your async code much easier to read and maintain.

    info> Note: The async and await keywords were introduced with ES7 – so make sure that you are using a browser or Node version that supports the ES7 JavaScript specification (or use a transpiler) if you want to use these keywords apart from this lab.

    It is quite simple to use async and await. There are a few rules to follow however:

    You mark a function async. This is “syntactic sugar” that tells the JavaScript runtime that this function will return a Promise.

    You can only use the await keyword within a function that is marked async. The exception to this is if you are working with ESM (ES6 Modules) natively as these let you use what is known as “top level await” - functionality that essentially lets you use the await keyword outside of an async function directly.

    info> Note: Under the hood, the await keyword uses something called a “generator”. The use of JavaScript generators and the yield syntax is not often used directly, but if you’d like to learn more than I recommend that you check out the MDN docs on the subject.

    Often, many organizations will seek to refactor Promise-based code to async/await syntax. You will explore this approach in this step. Creating an asynchronous function is very straightforward. The async keyword must be used before the function definition. When using the function keyword you would create an asynchronous function with the syntax: async function <function-name>. For arrow functions it’s a bit different and looks like this: async () => {}. You have an async function but there’s no point to using async without its counterpart - await. The purpose of await is to provide a synchronous looking syntax for asynchronous code which is very helpful for the readability and maintainability of your codebase. It makes your code much easier to reason about. Using await also ensures that any code below the use of await will only execute after the asynchronous code is finished running. It is very common out in the real world to encounter Promise-based code that needs to be refactored to async/await syntax so that codebases are both up to date with more modern JavaScript syntax and easier to read/maintain.

  5. Challenge

    ### Conclusion

    Nicely done! You’ve made it to the end of this guided code lab on async programming in JavaScript.

    In summary, you learned:

    • Async programming via callbacks/event handlers and setTimeout
    • Async programming with the Promise API
    • Using the async/await syntax to write clean, asynchronous code

    As follow on learning, I recommend that you learn about JavaScript generators as they form the basis of the async/await syntax and are another more obscure form of async programming in JavaScript.

    From here, you can be confident when it comes to async programming with JavaScript. I recommend that you continue your journey learning JavaScript by pursuing both video courses and more guided code labs here, at PluralSight.

Zach is currently a Senior Software Engineer at VMware where he uses tools such as Python, Docker, Node, and Angular along with various Machine Learning and Data Science techniques/principles. Prior to his current role, Zach worked on submarine software and has a passion for GIS programming along with open-source software.

What's a lab?

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Provided environment for hands-on practice

We will provide the credentials and environment necessary for you to practice right within your browser.

Guided walkthrough

Follow along with the author’s guided walkthrough and build something new in your provided environment!

Did you know?

On average, you retain 75% more of your learning if you get time for practice.