JavaScript async-await: The Complete Guide

1
267
Javascript ES7 - Async Await Tutorial With Example From Scratch

Javascript async-await feature is used to deal with asynchronous code in javascript. The async-await is a new technique to write async code in sync function. You can write it as a simple synchronous function. That makes it very popular and understandable. Node.js is now supporting async-await out of the box.

Async-await is a new way to write asynchronous code. Previous options for asynchronous code are callbacks and promises. Async/await built on top of promises. It cannot be used with plain callbacks or node callbacks.

Prerequisites

You can read the following tutorials if you do not know anything about Javascript callbacks and promises.

Promises in ES6

Callback in JavaScript

Introduction

ES 2017 introduced Asynchronous functions.

Async functions are a cleaner way to work with asynchronous code in JavaScript.

If we want to understand precisely what async-await is and how they work, we first need to understand Promises.

Javascript Promise

A promise is an object that may produce a single value some time in the future: either a resolved value or a reason that it’s not resolved (e.g., a network error occurred).

A promise may be in one of 3 possible states: fulfilled, rejected, or pending.

Promise users can attach callbacks to handle the fulfilled value or the reason for rejection.

Promises are eager, meaning a promise will start doing whatever task you give it as soon as the promise constructor is invoked. 

Now, let’s understand the async functions in js.

Async functions in Javascript

The word “async” before a function means one simple thing: a function always returns the Promise. Other values are wrapped in a resolved promise automatically.

// app.js

async function mando() {
  return 'baby yoda';
}
mando().then(data => {
  console.log(data)
})

Output

➜  es git:(master) ✗ node app
baby yoda
➜  es git:(master) ✗

So, the async function ensures that the function returns a promise.

We need to resolve the Promise, and then we will get the value.

Now, let’s understand what is await in Javascript.

Await in Javascript

The keyword await JavaScript to wait until that Promise settles and returns its result.

// app.js

async function mando() {
  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("Baby Yoda"), 1000)
  });
  let result = await promise;
  console.log(result);
}

mando();

 Output

➜  es git:(master) ✗ node app
Baby Yoda
➜  es git:(master) ✗

The mando() function returns the Promise, and the await keyword makes javascript wait for 1 second, and after that, it will return the value, “Baby Yoda”.

The function execution “pauses” because of setTimeOut() and resumes when the Promise settles, with the result becoming its result. So the code above shows “Baby Yoda” in one second.

Let’s understand what happened:  JavaScript waits until the Promise settles and then goes on with the result. That doesn’t cost any CPU resources because the engine can do other jobs: execute other scripts, handle events, etc.

It’s just a more elegant syntax of getting the promised result than a promise.then(), more comfortable reading and writing.

If we try to use await in the non-async function, there would be a syntax error:

function mando() {
  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("Baby Yoda"), 1000)
  });
  let result = await promise;
  console.log(result);
}

mando();

 Output

➜  es git:(master) ✗ node app
/Users/krunal/Desktop/code/node-examples/es/app.js:5
  let result = await promise;
               ^^^^^

SyntaxError: await is only valid in async function
    at wrapSafe (internal/modules/cjs/loader.js:1050:16)
    at Module._compile (internal/modules/cjs/loader.js:1098:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1167:10)
    at Module.load (internal/modules/cjs/loader.js:983:32)
    at Function.Module._load (internal/modules/cjs/loader.js:891:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47
➜  es git:(master) ✗

 We will get the syntax error because we can not use await in the simple JavaScript function.

The await won’t work in the top-level code. See the following code.

// app.js

let response = await fetch('https://api.github.com/users/KrunalLathiya')
let user = await response.json()
console.log(user)

 Output

➜  es git:(master) ✗ node app
/Users/krunal/Desktop/code/node-examples/es/app.js:1
let response = await fetch('https://api.github.com/users/KrunalLathiya')
               ^^^^^

SyntaxError: await is only valid in async function
    at wrapSafe (internal/modules/cjs/loader.js:1050:16)
    at Module._compile (internal/modules/cjs/loader.js:1098:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1167:10)
    at Module.load (internal/modules/cjs/loader.js:983:32)
    at Function.Module._load (internal/modules/cjs/loader.js:891:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47
➜  es git:(master) ✗

 You must wrap it into an anonymous async function, like the following code.

// app.js

const fetch = require('node-fetch')
const dev = process.env.NODE_ENV !== 'production';

(async () => {
  let response = await fetch('https://api.github.com/users/KrunalLathiya')
  let user = await response.json()
  console.log(user.login)
})()

 You must install the node-fetch library from NPM to run the program above.

Output

➜  es git:(master) ✗ node app
KrunalLathiya
➜  es git:(master) ✗

Now, let’s understand JS async-await.

Javascript async-await Example

The async-wait is non-blocking.

Javascript async-await was created to simplify working with and writing chained promises.

Our function has the keyword async before it.

The await keyword can only be used inside functions defined with async

Any async function returns a promise implicitly, and the resolution value of the Promise will be whatever you return from the function.

// server.js

async function Crypto() {
   return 10000;
 }
 
Crypto().then(BTC => {
   console.log(BTC)
});

In the above example, It is quite clear that the async function will always return a promise, and if you have noted here, I have not used await yet.

Await function will always work inside the Async function. If you write await in the normal function, then, it will give us an error.

The keyword await JavaScript to wait until that Promise settles and returns its result. So it will pause until we can get our Promise resolved and returns the resolved value.

Class Methods as async

A class method can also be async. Just put async before it.

// server.js

class Crypto {
   async getCoin() {
     return await Promise.resolve(10000);
   }
 }
 
 new Crypto()
   .getCoin()
   .then(BTC => {console.log(BTC)});

Error handling

If a promise usually resolves, then await Promise returns the result. But in case of a rejection, it throws the error, just as if there were a throw statement at that line.

async function mando() {
  await Promise.reject(new Error("Ohh!! Something went wrong"));
}
mando()

 Output

➜  es git:(master) ✗ node app
(node:58165) UnhandledPromiseRejectionWarning: Error: Ohh!! Something went wrong
    at mando (/Users/krunal/Desktop/code/node-examples/es/app.js:2:24)
    at Object.<anonymous> (/Users/krunal/Desktop/code/node-examples/es/app.js:4:1)
    at Module._compile (internal/modules/cjs/loader.js:1128:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1167:10)
    at Module.load (internal/modules/cjs/loader.js:983:32)
    at Function.Module._load (internal/modules/cjs/loader.js:891:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47
(node:58165) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:58165) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
➜  es git:(master) ✗

 We can catch that error using the try…catch, the same way as a regular throw.

// server.js

const fetch = require('node-fetch');

async function Crypto() {

   try {
     let response = await fetch('http://gibberissssh.com');
   } catch(err) {
     console.log(err);
   }
 }
 
 Crypto();

Switching from Promises to Async/Await.

We can use async/await in the following way.

function doubleAfter2Seconds(x) {
   return new Promise(resolve => {
     setTimeout(() => {
       resolve(x * 5);
     }, 2000);
   });
 }
 
 async function addAsync(x) {
   const a = await doubleAfter2Seconds(10);
   const b = await doubleAfter2Seconds(20);
   const c = await doubleAfter2Seconds(30);
   return x + a + b + c;
 }
 
 
 addAsync(100).then((sum) => {
   console.log(sum); //400
 });

In the above code, first, we have written the doubleAfter2Seconds() this function, which returns a promise. Then we have called this function in the async function and waiting until it resolves.

Then it will return a sum of that function. Finally!! You have just created an asynchronous function in JavaScript.

Conclusion

The async keyword before a function has two effects:

  1. It makes it always return a promise.
  2. Allows using await in it.

The await keyword before a promise makes JavaScript wait until that Promise settles, and then:

If it’s an error, the exception is generated as if a throwing error were called at that very place.
Otherwise, it returns the result.

Together they provide an excellent framework for writing asynchronous code that is easy to read and write.

With async-await, we rarely need to write Promise.then/catch, But we still shouldn’t forget that they are based on promises because sometimes (e.g., in the outermost scope), we have to use these methods. Also, Promise.all is a nice thing to wait for many tasks simultaneously.

That’s it for this tutorial.

See also

Node async-await

Javascript Promise.resolve()

Javascript Promise.reject()

Javascript Promise.all()

Javascript Promise.race()

1 Comment

Leave A Reply

Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.