Async/Await is a modern way to handle asynchronous operations in JavaScript. It is built on top of Promises and provides a cleaner, more readable syntax for writing asynchronous code that looks and behaves like synchronous code.
1. Understanding Async Functions
An async function is a function declared with the async keyword. Inside an async function, you can use the await keyword to pause the execution of the function until a Promise is resolved.
Syntax:
async function functionName() {
// Code to execute
}
In this example, the greet function is declared as async, and it returns a Promise that resolves to "Hello, World!".
2. The await Keyword
The await keyword can only be used inside an async function. It pauses the execution of the function until the Promise is resolved, returning the resolved value.
Example:
function fetchData() {
return new Promise(resolve => {
setTimeout(() => {
resolve("Fetched Data");
}, 2000);
});
}
async function processData() {
console.log("Start");
let data = await fetchData(); // Pauses until fetchData() is resolved
console.log(data); // "Fetched Data"
console.log("End");
}
processData();
In this example, await is used to wait for fetchData to resolve before logging the data.
3. Error Handling with Async/Await
You can use try/catch blocks to handle errors in async functions, just like you would in synchronous code.
Example:
async function processData() {
try {
let response = await fetch("<https://jsonplaceholder.typicode.com/users/1>");
if (!response.ok) {
throw new Error("Network response was not ok");
}
let data = await response.json();
console.log(data);
} catch (error) {
console.log("Error: " + error.message);
}
}
processData();
In this example, the try block contains the code that might throw an error, and the catch block handles any errors that occur.
4. Sequential and Parallel Execution
Async/await makes it easy to write code that runs sequentially. However, you can also run asynchronous operations in parallel for better performance.
Sequential Execution Example:
async function sequentialTasks() {
let result1 = await task1();
let result2 = await task2();
console.log(result1, result2);
}
async function task1() {
return new Promise(resolve => setTimeout(() => resolve("Task 1 Complete"), 1000));
}
async function task2() {
return new Promise(resolve => setTimeout(() => resolve("Task 2 Complete"), 2000));
}
sequentialTasks(); // Task 1 Complete, Task 2 Complete
Parallel Execution Example:
async function parallelTasks() {
let [result1, result2] = await Promise.all([task1(), task2()]);
console.log(result1, result2);
}
async function task1() {
return new Promise(resolve => setTimeout(() => resolve("Task 1 Complete"), 1000));
}
async function task2() {
return new Promise(resolve => setTimeout(() => resolve("Task 2 Complete"), 2000));
}
parallelTasks(); // Task 1 Complete, Task 2 Complete
In the parallel example, Promise.all runs task1 and task2 concurrently, improving performance.
5. Chaining Async Functions
You can chain async functions to create more complex workflows.
Example:
async function step1() {
return new Promise(resolve => setTimeout(() => resolve("Step 1 Complete"), 1000));
}
async function step2() {
return new Promise(resolve => setTimeout(() => resolve("Step 2 Complete"), 2000));
}
async function step3() {
return new Promise(resolve => setTimeout(() => resolve("Step 3 Complete"), 1500));
}
async function executeSteps() {
try {
let result1 = await step1();
console.log(result1);
let result2 = await step2();
console.log(result2);
let result3 = await step3();
console.log(result3);
} catch (error) {
console.log("Error: " + error.message);
}
}
executeSteps();
// Output:
// Step 1 Complete
// Step 2 Complete
// Step 3 Complete
In this example, each step waits for the previous one to complete before starting the next.
Use Case:
Suppose you're building a web application that needs to fetch user data and display it, but you also want to handle any potential errors gracefully.
document.getElementById("fetchButton").addEventListener("click", async function() {
try {
let response = await fetch("https://jsonplaceholder.typicode.com/users/1");
if (!response.ok) {
throw new Error("Network response was not ok");
}
let user = await response.json();
document.getElementById("output").innerHTML = `
<h2>${user.name}</h2>
<p>Email: ${user.email}</p>
<p>Phone: ${user.phone}</p>
`;
} catch (error) {
document.getElementById("output").innerHTML = "Error loading user data";
console.log("Error: " + error.message);
}
});
In this example:
Clicking the button fetches user data from an API asynchronously.
The fetched data is displayed in the output div.
Errors are handled gracefully with a try/catch block.
This use case demonstrates how to handle asynchronous operations in a web application using async/await, making your code more readable and maintainable. Understanding async/await is essential for modern JavaScript development, allowing you to write cleaner and more efficient asynchronous code.