Topics Covered
What is async?
What is await?
How does async/await work behind the scenes?
Example of using async/await
Error Handling
Interviews
Async/Await vs Promise.then/.catch
What is Async?
In JavaScript, async
is a keyword used before a function to create an asynchronous function.
Q: What is async?
A: Async is a keyword used before a function to create an async function.
Understanding Async Functions
An async
function always returns a promise. There are two cases:
Case 1: If you explicitly return a promise.
async function getData() {
return Promise;
}
Case 2: If you don't return a promise, it wraps the value inside a promise.
async function getData() {
return "javascript";
}
This function always returns a promise.
Now, calling this function:
async function getData() {
return "javascript";
}
const dataPromise = getData();
console.log(dataPromise); // Output: Promise {'javascript'}
To retrieve the value from the promise, you use the .then
method.
dataPromise.then((res) => console.log(res));
Remember, it always returns a promise in any case; keep this in mind for interviews.
Example of Returning a Promise
Let's create a promise and return it in an async function.
const p = new Promise((resolve, reject) => {
resolve("Promise Resolved values");
});
async function getData() {
return p;
}
const dataPromise = getData();
dataPromise.then((res) => console.log(res));
Handling Promises Before Async/Await
Before diving into async/await, let's understand how promises are handled without it.
const p = new Promise((resolve, reject) => {
resolve("Promise Resolved values");
});
function getData() {
p.then((res) => console.log(res));
}
getData();
Now, let's handle the same promise using async/await.
const p = new Promise((resolve, reject) => {
resolve("Promise Resolved values");
});
async function handlePromise() {
const val = await p;
console.log(val);
}
handlePromise();
Deep Dive into Async/Await
Consider a promise with a timeout:
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Promise Resolved values");
}, 10000);
});
async function handlePromise() {
const val = await p;
console.log(val);
}
handlePromise();
In normal handling, JavaScript doesn't wait for the promise to be resolved and moves on to the next line. However, with async/await, the engine waits for the promise to be resolved before moving to the next line.
Understanding Async/Await in Detail
Now, let's explore the difference between normal handling and handling using async/await.
async function handlePromise() {
console.log("hello world "); // Printed quickly
const val = await p; // Program waits for 10 seconds
console.log("javascript ");
console.log(val);
// What happens now? Interview question
// After 10 seconds, both promises will be printed
const val2 = await p;
console.log("javascript ");
console.log(val2);
}
handlePromise();
So, the JavaScript engine is waiting for the promise to be resolved, and once resolved, it moves on to the next line.
Handling Multiple Promises
Now, let's handle two promises:
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Promise Resolved values");
}, 10000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Promise Resolved values");
}, 5000);
});
async function handlePromise() {
console.log("hello world "); // Printed quickly
const val = await p1; // Program waits for 10 seconds
console.log("javascript ");
console.log(val);
const val2 = await p2;
console.log("javascript ");
console.log(val2);
}
handlePromise();
After 5 seconds, nothing will be printed, but after 10 seconds, both promises will be resolved and printed.
Understanding Async/Await Execution Flow
When this function is executed, it will go line by line as JavaScript is a synchronous, single-threaded language. Let's observe what happens under the call stack. Breakpoints have been set for clarity.
// Call stack flow -> handlePromise() is pushed
// It will log `Hi` to the console
// Next, it encounters await where a promise is supposed to be resolved
// Will it wait for the promise to resolve and block the call stack? No
// Thus, handlePromise() execution gets suspended and moved out of the call stack
// When JavaScript encounters the await keyword, it suspends the function's execution until the promise is resolved
// So, `p` will get resolved after 5 seconds, and handlePromise() will be pushed to the call stack again after 5 seconds
// It will start executing from where it left off
// Now, it will log 'Hello There!' and 'Promise resolved value!!'
// Then, it will check whether `p2` is resolved or not
// It will find that since `p2` will take 10 seconds to resolve, the same process will repeat
// Execution will be suspended until the promise is resolved
// Thus, JS is not waiting, and the call stack is not getting blocked.
// Moreover, in the above scenario, what if p1 takes 10 seconds and p2 5 seconds?
// Even though p2 got resolved earlier, JS is a synchronous, single-threaded language
// It will first wait for p1 to be resolved and then will immediately execute all.
Real-life Example - Fetch API
async function handlePromise() {
// fetch() => Response Object, which has a body as a Readable stream => Response.json() is also a promise, which when resolved => value
const data = await fetch("https://api.github.com/users/jatin4224"); // Waited for this line to be resolved
const res = await data.json(); // Waited for this data JSON to be resolved
console.log(res);
}
handlePromise();
Error Handling
Error handling in async/await is achieved using try-catch:
async function handlePromise() {
try {
const data = await fetch("https://api.github.com/users/jatin4224");
const res = await data.json();
console.log(res);
} catch (err) {
console.log(err);
}
}
handlePromise();
Another way to handle errors is using .catch
:
handlePromise().catch((err) => console.log(err));
Handling an invalid API URL:
async function handlePromise() {
try {
const data = await fetch("https://invalid");
const res = await data.json();
console.log(res);
} catch (err) {
console.log(err);
}
}
handlePromise();
handlePromise().catch((err) => console.log(err));
Interview Tips
When asked about async/await, explain the concepts covered in this blog with examples. Be prepared to explain promises if required.
Async/Await vs Promise.then/.catch
Async/await is essentially syntactic sugar around promises. It solves some of the shortcomings of promise chaining and enhances code readability. It is advisable to use async/await as it simplifies code structure and improves clarity.