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.

<button onclick="handleClick()">Click me</button>
<div id="post"></div> <!-- placeholder for the post -->
<ul id="comments"></ul> <!-- placeholder for the comments -->
view raw markup.html hosted with ❤ by GitHub

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.)

function fetchPost() { // AJAX request #1
performance.mark('fetchPostStart');
return fetch('https://jsonplaceholder.typicode.com/posts/1').then((res) => {
performance.mark('fetchPostEnd');
performance.measure('fetchPost', 'fetchPostStart', 'fetchPostEnd');
return res.json();
});
}
function fetchComments() { // AJAX request #2
performance.mark('fetchCommentsStart');
return fetch('https://jsonplaceholder.typicode.com/posts/1/comments').then((res) => {
performance.mark('fetchCommentsEnd');
performance.measure('fetchComments', 'fetchCommentsStart', 'fetchCommentsEnd');
return res.json();
});
}
function someSyncOperation() { // Expensive synchronous operation
performance.mark('someSyncOperationStart');
for (let i = 0; i < 2500000000; i++) {}
performance.mark('someSyncOperationEnd');
performance.measure('someSyncOperation', 'someSyncOperationStart', 'someSyncOperationEnd');
}
function appendPostDOM(postJson) {
// not important, just helper method to insert the AJAX response into the DOM
}
function appendCommentsDOM(commentsJson) {
// not important, just helper method to insert the AJAX response into the DOM
}
view raw functions.js hosted with ❤ by GitHub

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);
}
Liked what you've read?
Follow me on LinkedIn!