Async/Await in JS
ECMAScript 2017 brought in syntactic sugar on top of Promises
in JavaScript in form of async
and await
. Async/await provides a special syntax to work with promises in a more comfortable fashion. They allow us to write asynchronous Promise-based code in a synchronous manner.
Before proceeding with async/await you need to understand Promises. You can head over to Promises in JS article to read and learn about promises.
Async functions
The async function declaration defines an asynchronous function. An asynchronous function is a function which operates asynchronously via the event loop.
Keyword async
can be placed before a function:
async function f() {
// function-body
}
An “async” function always returns a promise. If the returned value is non-promise
then JavaScript wraps it in an implicit Promise
.
Example:
async function f() {
return 1;
}
f().then(result => {
console.log(result)
})
// Output: 1
Async
function returns aPromise
which will be resolved with the value returned by the async function or rejected with an uncaught exception thrown from within the async function.
Await
The await
operator is used to wait for a Promise
. It can only be used inside an async
function.
let value = await promise
The await
expression causes the async
function to pause execution until the Promise
is settled.
The value of await
expression is that of resolved promise. If Promise
is rejected, await throws
the rejected value.
Example from MDN:
|
|
The function f1
execution pauses at line 10 and resumes when the promise is settled. The variable x
has the result of the resolved promise that is, 10
.
Await
is a more polished syntax of getting promise result as compared to Promise.then
. We can chain promises more elegantly with await
than Promise.then()
.
Insted of this:
doAsync1.then(function(result) {
return doAsync2(result)
})
.then(function(result) {
return doAsync3(result)
})
.then(function(result) {
console.log('Got the final result')
})
.catch(failureCallback);
We can do this:
async function f() {
let v1 = await doAsync1()
let v2 = await doAsync2(v1)
let v3 = await doAsync3(v2)
console.log('Got the final result')
}
f()
Await
won’t work in top-level code. It can also be used inside anasync function
.
Error Handling
If a promise is fulfilled then await returns its result. But in the case of rejection, it throws
an error. We can use simple try...catch
statement to catch
the error.
var f = async function() {
try {
let v = await Promise.reject(new Error("Invalid Statement"))
} catch(err) {
console.log(err.message)
}
}()
// Output: "Invalid Statement"
Handling rejected Promise
without try block.
var f = async function() {
let result = await Promise.reject(new Error("Invalid Statement"))
}
// result will be undefined if the promise is rejected
f().catch(err => {
console.log(err.message)
})
// Output: "Invalid Statement"
Example
Callbacks are not interchangeable with Promises. This means that callback-based APIs cannot be used as Promises
.
But we can wrap a library using callback-based API inside a Promise
.
Example:
const request = require('request')
function get_response() {
return new Promise((resolve, reject) => {
request.get(options, (err, res, body) => {
if(err)
reject(err)
else
resolve(JSON.parse(body))
})
})
}
request
library uses callbacks. Here we wrap it inside a function returning Promsie
. The function is then responsible for calling resolve when it’s done or reject if there are errors.
Now we can use .then()
or await
on the returned Promise
.
var options = {
url: 'https://api.github.com/users/rishabhc32',
headers: {
'User-Agent': 'request'
}
}
get_response()
.then((result) => {
console.log(result)
})
.catch((err) => {
console.log('Error occured')
})
let another_response = async function() {
options.url = 'https://api.github.com/users/mittalprince'
let result = await get_response()
console.log(`login id: ${result.login} \nid: ${result.id}`)
options.url = 'malformed-url'
try {
let result = await get_response()
} catch(err) {
console.log(`Error: ${err.message}`)
}
}()