JavaScript async/await Cheat Sheet

The async and await keywords introduced in JavaScript make it easier to write asynchronous code that looks like synchronous code and is a bit less boilerplate-y. They are really just a small abstraction over promises though so if you don’t understand promises read this first.

An async function that returns a value

The async implementation of foo above is equivalent to the below implementation that explicitly returns a Promise

Using await keyword to block execution until a promise fulfills

Keep in mind that await can only be used inside an async function — hence why we use .then().catch() when calling bar() but inside bar() we can await the result of foo().

An async function that throws an Error

Again, the async implementation of foo above is equivalent to this version using Promise.

Handling Errors with try…catch

In the example below bar() wraps a call to await foo() with try...catch. Since foo() throws an Error i.e., the promise rejects, the result of await foo() on line 7 is an Error so execution will fall in to the catch() block.


await doesn’t block all the way up the call stack

You might not expect bar() done to be printed after foobar() done. Let’s walk through what’s happening here

  1. We start by calling await foo() on line 20 which tells JavaScript to wait until foo() fulfills its promise before executing the rest of foobar().
  2. foo() begins executing and the first thing it does is await sleep(4000). on line 8. This tells JavaScript to wait until sleep() fulfills its promise before executing the rest of foo().
  3. After 4 seconds have passed sleep() fulfills and foo() can continue. It executes line 9 and prints foo() done to STDOUT and then returns foo.
  4. Since foo() has fulfilled its promise foobar() can now finish executing line 20 and prints foo — the value returned from foo() to STDOUT.
  5. foo() continues on to line 22 and calls bar(). Notice that it doesn’t use the await keyword. Logically this is a bug — we’ve forgotten to tell JavaScript to wait until bar() fulfills its promise before moving on. So what is printed to STDOUT is Promise { <pending> } because that’s the value of bar() when it’s first created (remember a Promise is a container for some future value that has three states — pending, fulfilled, rejected).
  6. There is nothing left to do in foobar() so it returns and the .then() callback on line 26 is executed printing foobar() done to STDOUT
  7. bar() is still executing in the background and 1s later prints bar() done to STDOUT


What’s happening here is without using await to block until foo() completes JavaScript evaluates line 7 to be a pending Promise. Since that’s not an Error and there is no other code to evaluate bar() completes. But foo() is still processing in the background so when it does eventually throw an Error there is nothing to handle it hence the UnhandledPromiseRejectionWarning.

If you’re surprised that the callback in bar().catch(e => console.error(e)); didn’t catch the Error thrown by foo() remember that bar() technically fulfilled its promise and so bar().catch() was never called. Instead the callback inside bar().then(() => console.log('bar() done') was called . That’s why we see bar done() printed to STDOUT.

Senior software engineer at Skyscanner. I mostly write Java and Python back end systems occasionally foraying in to JavaScript. Also a dad and 49ers fan.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store