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
await
outside of anasync
function?
You'll get a syntax error. Theawait
keyword can only be used inside anasync
function. -
Can I use
async/await
withPromise.all()
?
Yes, you can. UsingPromise.all()
withasync/await
allows 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/await
is done using standardtry...catch
blocks. Wrap theawait
call in atry
block, and catch any errors in thecatch
block. Unhandled rejections from Promises will still cause errors if not caught.