JavaScript tutorials > Advanced Concepts > Asynchronous JavaScript > How do async and await work in JavaScript?
How do async and await work in JavaScript?
Learn how async and await simplify asynchronous JavaScript code, making it easier to read and manage. This tutorial covers the basics, real-world examples, and best practices for using async/await effectively.
Introduction to Async/Await
async and await are syntactic sugar built on top of Promises in JavaScript. They make asynchronous code look and behave a bit more like synchronous code, which improves readability and maintainability. The async keyword is used to define an asynchronous function, and the await keyword is used inside an async function to pause execution until a Promise is resolved.
Basic Async Function Example
This example shows a basic async function. The async keyword before the function declaration indicates that the function will always return a Promise. If the function returns a value that is not a Promise, JavaScript automatically wraps it in a resolved Promise.
async function myFunction() {
return 'Hello, world!';
}
myFunction().then(value => console.log(value)); // Output: Hello, world!
Using Await with Promises
The await keyword can only be used inside an async function. It pauses the execution of the async function until the Promise following it is resolved. In this example, await fetch(...) waits for the fetch Promise to resolve, and then await response.json() waits for the JSON parsing to complete. Error handling is done with the standard try...catch block.
async function fetchData() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const data = await response.json();
console.log(data); // Output: { userId: 1, id: 1, title: 'delectus aut autem', completed: false }
} catch (error) {
console.error('Error fetching data:', error);
}
}
fetchData();
Concepts Behind the Snippet
async/await simplifies asynchronous operations by:
try...catch blocks instead of Promise-specific .catch() methods.
Real-Life Use Case Section
This example demonstrates a common use case: fetching data from multiple APIs sequentially. Instead of nesting .then() calls or using Promise.all for concurrent requests, async/await makes the flow much clearer. It simulates fetching a user, then their posts, and finally comments on the first post, all in a sequential and readable manner.
async function processData() {
try {
const user = await getUser(123);
const posts = await getPostsForUser(user.id);
const comments = await getCommentsForPost(posts[0].id);
console.log('Comments:', comments);
} catch (error) {
console.error('Error processing data:', error);
}
}
async function getUser(id) {
// Simulate fetching user data from an API
return new Promise(resolve => {
setTimeout(() => {
resolve({ id: id, name: 'John Doe' });
}, 500);
});
}
async function getPostsForUser(userId) {
// Simulate fetching posts for a user from an API
return new Promise(resolve => {
setTimeout(() => {
resolve([{ id: 1, title: 'My First Post', userId: userId }]);
}, 500);
});
}
async function getCommentsForPost(postId) {
// Simulate fetching comments for a post from an API
return new Promise(resolve => {
setTimeout(() => {
resolve(['Great post!', 'Interesting insights.']);
}, 500);
});
}
processData();
Best Practices
await calls in try...catch blocks to handle potential errors gracefully.Promise.all() when multiple asynchronous operations can run in parallel to improve performance.async/await in situations where simpler Promise chaining is sufficient.
Interview Tip
Be prepared to explain the difference between async/await and Promises. Highlight that async/await is syntactic sugar that makes asynchronous code easier to read and write, but it still relies on Promises under the hood. Also, know how to handle errors in async/await functions using try...catch blocks.
When to use them
Use async/await when you need to write asynchronous code that reads like synchronous code, especially when dealing with sequential asynchronous operations. It's particularly useful when you have multiple dependent asynchronous tasks that need to be executed in a specific order.
Memory footprint
While async/await improves readability, it doesn't inherently reduce the memory footprint compared to Promises. Both use the event loop and callback functions. Improper handling of Promises or async/await can lead to memory leaks, such as unhandled rejections or circular dependencies. Ensure proper error handling and avoid creating unnecessary closures to optimize memory usage.
Alternatives
Alternatives to async/await include:
.then() and .catch() for handling asynchronous operations.
Pros
try...catch blocks for error handling.
Cons
await keyword can only be used inside async functions.
FAQ
-
What happens if I use
awaitoutside of anasyncfunction?
You'll get a syntax error. Theawaitkeyword can only be used inside anasyncfunction. -
Can I use
async/awaitwithPromise.all()?
Yes, you can. UsingPromise.all()withasync/awaitallows you to perform multiple asynchronous operations concurrently. For example:async function processData() { const [user, posts] = await Promise.all([ getUser(123), getPostsForUser(123) ]); console.log('User:', user); console.log('Posts:', posts); } -
How does error handling work with
async/await?
Error handling inasync/awaitis done using standardtry...catchblocks. Wrap theawaitcall in atryblock, and catch any errors in thecatchblock. Unhandled rejections from Promises will still cause errors if not caught.