Async/await is awesome, but don’t abandon Promises

Async/await is awesome, but don’t abandon Promises
Shoutout to Cassidy Williams 😀

Let’s create a button that will:

  • perform an expensive synchronous operation,
  • fire 2 AJAX requests, and
  • update the DOM based on the AJAX responses.

Here is the markup.

Here are the functions. Let’s also measure the duration of each operation with the Performance API, which visualizes when and how long each function executes on the Chrome DevTools Performance Timeline. (Thanks to JSONPlaceholder for the dummy endpoints.)


You’re still here? Good, here comes the interesting part: writing the onclick handler for the button. Since all the cool kids are doing it, let’s use async / await.

async function handleClick() {
   someSyncOperation(); // Expensive sync operation 

   const postJson = await fetchPost(); // AJAX request #1

   const commentsJson = await fetchComments(); // AJAX request #2

   appendPostDOM(postJson);
   appendCommentsDOM(commentsJson);
}

Here is the Performance Timeline after clicking the button.

Let’s take a closer look.

Makes sense, plenty of articles out there about how async / await turns asynchronous code into blocking code. FYI, each bar is about 2 seconds when throttling the network to “Slow 3G”.

So a total execution time of 6 seconds.


OK. The fetchPost and fetchComments can be executed in parallel, so let’s use the await Promise.all combo.

async function handleClick() {
  someSyncOperation();

  const [postJson, commentsJson] = await Promise.all([
    fetchPost(), 
    fetchComments()
  ]);

  appendPostDOM(postJson);
  appendCommentsDOM(commentsJson);
}

The total execution time is now 4 seconds since fetchPost and fetchComments execute in parallel.


OK. Since someSyncOperation is not dependent on the AJAX requests, let’s see if moving it to the last line in the function speeds things up.

async function handleClick() {
  const [postJson, commentsJson] = await Promise.all([
    fetchPost(), 
    fetchComments()
  ]);

  appendPostDOM(postJson);
  appendCommentsDOM(commentsJson);

  someSyncOperation();
}

Nope, the total execution time is still 4 seconds.


OK. It’s time to go “full Promise”.

function handleClick() {
  Promise.all([
    fetchPost(),
    fetchComments()
  ]).then(([postJson, commentsJson]) => {
    appendPostDOM(postJson);
    appendCommentsDOM(commentsJson)
  });

  someSyncOperation();
}

Going “full Promise” cuts total execution time to 2 seconds.

The reason why this works deserves its own article, but here is an awesome explainer.

Today I learned.


Source: Programmer Humor

Bonus

For the die-hard async / await fans out there, I learned (literally on the day of writing this story) that the following snippet actually does the same thing. Credit to this article by Moon.

async function handleClick() {
  const postPromise = fetchPost();
  const commentsPromise = fetchComments();
  
  someSyncOperation();
  const postJson = await postPromise;
  const commentsJson = await commentsPromise;

  appendPostDOM(postJson);
  appendCommentsDOM(commentsJson);
}

📫 Hit me up on LinkedIn or Twitter!