What is syntactic sugar?
In computer science, a syntax or feature designed to make a piece of code easier to express or to read within a programming language is referred to as syntactic sugar.
It makes the language "sweeter" for human use: things can be expressed more clearly, more concisely, or in an alternative style that some may prefer.
Basics of Async/Await
The Async function
and the await
keyword were introduced with the ES8 (2017) release, as an extension to promises. They were introduced to make promise-based asynchronous programing more readable and easier to perceive. Async/await simplifies the syntax used to consume promise-based APIs.
If you yet to encounter promises or you yet to fully understand working with promises, you should check out my article on Making Promises in JavaScript.
Split into two parts; the async
keyword that is appended in front of a function to make sure the function only returns a promise, and the await
keyword that can only be used within async functions.
Async functions
Appending the async
keyword in front of a function means the function will always return a promise.
//A simple function that returns a value
function echo () {
return 'Hello World';
}
echo();
Adding the async
keyword to the above function will make it return a promise instead of returning the value.
//A async function that returns a promise
async function echo () {
return 'Hello World';
}
echo();
Declaring async functions
Just like we have used function declaration to declare our echo
function example above, we can also declare our async functions using;
Async function expression, as below:
let echo = async function () {
return 'Hello World';
}
echo();
And we can use arrow functions:
let echo = async () => {
return 'Hello World';
}
echo();
Working with the returned value when the promise is fulfilled
We can append the .then()
method to the promise as we have seen with promise chains, we can use the value to do something else, as seen below:
let echo = async () => {
return 'Hello World';
}
//appending a .then() method to work with the value
echo().then(response => {
console.log('I am shouting a big ' + response + '!!')
})
The await keyword
Thing to seriously note: The
await
keyword can only be used within async functions. The only time it works on its own is when used with Javascript modules.
await
can be used to call any asyn promise-based function that returns a promise. It literally pauses the async function block(not the entire code execution) till the promise returns a value.
Basic syntax:
let value = await promise;
A basic use case is as seen below:
async function sampleFunc () {
let newPromise = new Promise((resolve, reject) => {
resolve('The block of code is complete.');
})
let displayMessage = await newPromise;
console.log(displayMessage);
}
sampleFunc();
The await
can be used in place of the .then()
method to handle resolved promises.
//A simple promise to fetch data from the jsonplaceholder
fetch("https://jsonplaceholder.typicode.com/users")
.then(resp => resp.json())
.then(result => {
console.log(result)
})
Let us try rewriting the above example with async/await:
//Using the async/await syntax
async function getData () {
let fetchedData = await fetch("https://jsonplaceholder.typicode.com/users")
let parsedData = await fetchedData.json()
console.log(parsedData)
}
We can see that it makes our code clearer and easy to read.
Using await
with the setTimeout
API
async function newFunc () {
let samplePromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('I have been resolved')
}, 5000)
});
let getResolved = await samplePromise;
console.log(getResolved);
}
newFunc();
Using Async/Await with promise features that handle iterables
Given that async/await built on top promises, they are compatible with all promise features including the likes of Promise.all
, Promise.allSettled
, Promise.any
and Promise.race
.
const promise1 = new Promise(resolve => {
setTimeout(() => resolve('success-1'), 2000)
})
const promise2 = new Promise(resolve => {
setTimeout(() => resolve('success-2'), 7000)
})
const promise3 = new Promise(resolve => {
setTimeout(() => resolve('success-3'), 9000)
})
const data = await Promise.all([ promise1, promise2, promise3 ]);
console.log(data)
Using async/await to fetch a set data from an API:
const urls = [
"https://jsonplaceholder.typicode.com/users",
"https://jsonplaceholder.typicode.com/posts",
"https://jsonplaceholder.typicode.com/albums",
];
async function grabData () {
const [ users, posts, albums ] = await Promise.all(
urls.map(async function (url) {
const fetchedData = await fetch(url);
return fetchedData.json();
}),
);
console.log('Users', users),
console.log('Posts', posts),
console.log('Albums', albums)
}
Error handling
When handling errors in async/await there are different paths to go.
One of the more common ways of handling errors with async/await is using the try...catch structure.
Let us try including a try
and a catch
block to our grabData
example above.
// We are grabbing a set of data from the jsonplaceholder and logging it in our browser console
const urls = [
"https://jsonplaceholder.typicode.com/users",
"https://jsonplaceholder.typicode.com/posts",
"https://jsonplaceholder.typicode.com/albums",
];
async function grabData () {
try {
const { users, posts, albums } = await Promise.all(
urls.map(async function (url) {
const fetchData = await fetch(url);
return fetchData.json();
}),
);
console.log('Users', users),
console.log('Posts', posts),
console.log('Albums', albums)
} catch (err) {
console.error(err)
}
}
Since async/await was create upon promises, you can also follow the hybrid path by chaining the .catch()
method instead of the try...catch structure.
const urls = [
"https://jsonplaceholder.typicode.com/users",
"https://jsonplaceholder.typicode.com/posts",
"https://jsonplaceholder.typicode.com/albums",
];
async function grabData () {
const { users, posts, albums } = await Promise.all(
urls.map(async function (url) {
const fetchData = await fetch(url);
return fetchData.json();
}),
);
console.log('users', users),
console.log('posts', posts),
console.log('albums', albums)
}
grabData()
.catch(err => console.log(err))
Conclusions
My conclusions on using async/await are as follows:
Using async/await makes our asynchronous promise-based API consumption a lot more readable and expressable.
A downside to using async/await is that it makes our code behave synchronously.
await
literally pauses any code that comes after it till the previous block is done. This can make your program run slower when chaining a set ofawait
blocks.Using async/await is more a matter of what is comfortable for you and your team.
And that brings us to the end of this article, if you have any form of feedbacks please drop it down in the comments, and if you find this article helpful do drop an appreciation.