Async and Await

We saw how promises are superior to callbacks, but there’s one drawback to using promises. The structure of synchronous imperative code is drastically different from the structure of asynchronous code that uses promises. Unlike the sequential code that flows naturally from one statement or expression to the next, we have to get our head wrapped around the then() and the catch() sequence.

The async and await feature was introduced to keep the code structure identical between synchronous and asynchronous code. This does not affect the way we write asynchronous functions, but it largely changes the way we use them.

There are two rules to using this feature:

  • To be able to use an asynchronous function as if it were a synchronous function, optionally mark the promise-returning asynchronous function with the async keyword.

  • To call the asynchronous function as if it were a synchronous function, place the await keyword right in front of a call. The await keyword may be used only within functions marked async.

Let’s explore this feature by first comparing synchronous to asynchronous code structure. Then we’ll look at how async and await help.

Let’s create two functions—one synchronous and one asynchronous—they both take a number and return the double of the number.

 const​ computeSync = ​function​(number) {
 if​(number < 0) {
 throw​ ​new​ Error(​'no negative, please'​);
  }
 return​ number * 2;
 };
 
 const​ computeAsync = ​function​(number) {
 if​(number < 0) {
 return​ Promise.reject(​'no negative, please'​);
  }
 return​ Promise.resolve(number * 2);
 };

computeAsync() is the asynchronous counterpart to the synchronous computeSync(). If the given parameter is negative, the synchronous function throws an exception while the asynchronous function returns a promise that rejects.

Here are two functions that call the previous two functions. The first function, callComputeSync, makes synchronous calls to computeSync(). The second function, callComputeAsync(), makes asynchronous calls to computeAsync().

 const​ callComputeSync = ​function​(number) {
 try​ {
 const​ result = computeSync(number);
  console.log(​`Result is ​${result}​`​);
  } ​catch​(ex) {
  console.log(ex.message);
  }
 }
 
 const​ callComputeAsync = ​function​(number) {
  computeAsync(number)
  .then(result => console.log(​`Result is ​${result}​`​))
  .​catch​(err => console.log(err));
 }

The code structure is quite different between the synchronous version and the asynchronous version. We can make the code structure look the same by using async and await.

Let’s see how to make the asynchronous call structurally look like a synchronous call—it will run like the second call but look and feel like the first one. Let’s copy over the callComputeSync() function to a new function, callCompute(), and make two changes like so:

 const​ callCompute = ​async​ ​function​(number) {
 try​ {
 const​ result = ​await​ computeAsync(number);
  console.log(​`Result is ​${result}​`​);
  } ​catch​(ex) {
  console.log(ex);
  }
 }

The only difference between callComputeSync() and callCompute() is that the second function is marked as async and, where the first function called computeSync(), the second calls computeAsync() with the keyword await prefixed.

When an asynchronous function call is prefixed with await, JavaScript will make the call to the function and suspend execution of the current function flow, to wait for the promise to resolve or reject. If the promise resolves, then it moves forward. If the promise rejects, then it jumps into the catch block of the surrounding try-catch block. The suspension of execution upon reaching an await is much like the suspension of execution when execution reaches the yield keyword we saw in Using Yield.

While the promise is a surefire replacement for callbacks, async-await is not a replacement but an enhancement. Here are some things to consider when deciding to use then-catch compared to async-await:

  • If the code is not in an async function, then you can’t use await; prefer then-catch in this case.

  • If you are converting a legacy synchronous code to make it asynchronous, await will preserve the code structure, compared to then-catch, which will totally change the code structure.

  • It is often easier to prototype a synchronous version of a function and then convert it to the asynchronous version when needed. In this case again, async-await shines compared to then-catch.

  • Then then-catch syntax may be more suitable when creating functional style code and the async-await more suitable when writing imperative style code. At the same time, use caution—async-await may be error prone if the function that we are waiting for completion modifies a shared state; such state change may make the suspended code vulnerable.

Use promises to create asynchronous functions and then pick between then-catch and async-await for calling asynchronous functions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.61.187