Luc Claustres

Digital Craftsman, Co-Founder of Kalisio, Ph.D. in Computer Science

A common misconception about async/await in JavaScript

With promises we write asynchronous code that emulates synchronous code but with async/await we write asynchronous code that looks like synchronous code. As a consequence this often leads to misconception.
I decided to write this article because since the introduction of async/await I have noted that a lot of developers have more difficulties understanding what is executed asynchronously and synchronously. These difficulties were already around with promises but async/await have emphasized it by coming closer to synchronous programming.
Let’s work with a simple example that writes a message before then after an asynchronous operation. We will implement it using async/await and a promise:
const delay = (duration) =>
  new Promise(resolve => setTimeout(resolve, duration))

async function asyncWithAwait(prefix) {
  console.log(prefix + ' before await')
  await delay(1000)
  console.log(prefix + ' after await')

function asyncWithPromise(prefix) {
  console.log(prefix + ' before promise')
  return delay(1000)
  .then(_ => console.log(prefix + ' after promise'))
Some developers tend to believe that the function written with async/await will always be executed synchronously because the code looks like if we wait synchronously for the delayed operation before continuing the execution. So let’s run it with this code and compare with the promise version:
async function run() {
  let prefix = '(1)'
  console.log(prefix + ' with await')
  console.log(prefix + ' with promise')
  console.log(prefix + ' after all')

This is the output:
(1) with await
(1) before await
(1) with promise
(1) before promise
(1) after all
(1) after await
(1) after promise
As you can see the function code is run synchronously only until the first encountered await. Like with promises, it is up to the caller to decide if the code will be executed synchronously or not. Let’s look at the more synchronous version:
async function run() {
  let prefix = '(2)'
  console.log(prefix + ' with await')
  await asyncWithAwait(prefix)
  console.log(prefix + ' with promise')
  asyncWithPromise(prefix).then(_ => console.log(prefix + ' after all'))

And this is the output:
(2) with await
(2) before await
(2) after await
(2) with promise
(2) before promise
(2) after promise
(2) after all
If the caller does not make use of await(respectively then with promises) then your code will not run synchronously. That’s the reason why async/await does not work as expected with constructs like for Each.
If you liked this article, hit the applause button below, share with your audience, follow me on Medium or read more for insights about: not using console.log anymore, the Feathers framework, the raise and next step of Artificial Intelligence, the evolution of Computer Science, the goodness of unlearning and why software development should help you in life ?


More by Luc Claustres

Topics of interest