This chapter is included in the free preview


Asynchronous programming is everywhere in JavaScript. This is the result of the fundamental choices that define how it works. In other languages, you can use multiple threads and that allows synchronous waiting, a crucial feature missing from JavaScript. It is by design single-threaded and a wait operation stops everything, just think of the case where a long calculation freezes the UI.

Without a way to wait for a later result synchronously, JavaScript needs to use callbacks. Even simple things like waiting for a given duration requires a function that will be run when the time is up:

setTimeout(() => {
  console.log("1 second passed!");
}, 1000);

You can find this pattern everywhere, as most of the things are asynchronous in nature. Using fetch to make an HTTP call to a server is an async operation. Just like getting information about the available cameras and microphones with the getUserMedia call, as it needs to get permission from the user. Same with reading and writing files. While these have synchronous versions for now, they are terrible for performance. Or want to do some web scraping with Puppeteer? Every single instruction is asynchronous as all of them communicate with a remote process. Or for backend applications, reading or writing data to a database is also inherently async.

And not only that some functions are async but all the other functions that call them need to be async too. A piece of functionality that requires making a network call, for example, is asynchronous, no matter how insignificant that call is compared to what other things the function is doing. Because of this, almost all JavaScript applications consist of mostly asynchronous operations.

Over the years, the language got a lot of features that make writing async code easier. Gone are the days of the so-called callback hell where a series of callback-based async calls made the program's structure very hard to understand and easy to inadvertently silence errors.

First, Promises gained widespread support, flattening the structure of callbacks. Then async/await became the mainstream way of async programming, hiding a lot of the complexities of asynchronicity behind only two keywords: async and await.

This improved readability and made it a lot easier for new programmers to write asynchronous code. Modern JavaScript is still using the same single-threaded no-sync-wait event loop, but the program structure reflects that of a modern language.

But asynchronous programming is inherently hard. While the language helps with the syntax to make understanding and writing code easier, asynchronicity introduces a lot of potential errors, most of them so subtle they only occur in special circumstances.

Even though I've been working for many years with asynchronous code, some of the problems in this book took me a week to reach a solution I'm happy with. My goal with this book is that you'll have an easier time when you encounter similar problems by knowing what are the hard parts. This way you won't need to start from zero but you'll have a good idea of what are the roadblocks and the best practices.

You'll notice that error handling is a recurring topic in this book. This is because it is an often overlooked concept and that leads to code that easily breaks. By knowing how errors in async functions and Promises work you'll write safer programs.


This book is divided into two parts.

The first chapter, Getting started with async/await, is an introduction to async/await and Promises and how each piece of the async puzzle fit together. The primary focus is async functions as they are the mainstream way to program asynchronously in JavaScript. But async/await is a kind of magic without knowing about Promises, so you'll learn about them too.

By the end of the first chapter, you'll have a good understanding of how to use async functions and how they work under the hood.

The second part of the book consists of several common programming tasks and problems. You'll learn when that particular problem is important, how to solve it, and what are the edge cases. This gives you a complete picture so that when you encounter a similar problem you'll know how to approach it.

This book is not meant to be read from cover to cover but to be used as a reference guide. With the patterns described in this book, my hope is that you'll see the underlying difficulty with async programming so when you work on your own solutions you'll know the pitfalls and best practices so you'll end up with more reliable code.