The async and await keywords are part of modern JavaScript (introduced in ECMAScript 2017) that simplify working with asynchronous code. These keywords make asynchronous code look and behave more like synchronous code, improving readability and reducing the complexity of handling asynchronous operations, especially when combined with Promises.
In this explanation, I'll walk you through how async and await work in a real-time example. But before we dive into the example, let’s review the basic concepts.
1. What is async?
The async keyword is used to define an asynchronous function. It tells JavaScript that the function will return a Promise.
Inside an async function, you can use await to pause execution of the function until a Promise is resolved or rejected.
2. What is await?
The await keyword can only be used inside an async function. It pauses the execution of the async function until the Promise is resolved, and then returns the result.
If the Promise is rejected, it throws an error, which you can catch using try...catch.
Together, async and await make asynchronous code look more like synchronous code, which improves code readability and reduces the complexity of dealing with nested callbacks or Promise chaining.
3. Real-Time Example: Fetching Data from an API
Let’s consider a scenario where you're building a simple Node.js application that fetches data from an external API (for example, a weather API) and processes it.
Without async and await (using Promises):
Before diving into async and await, let's first look at how you would do this using Promises and then/catch:
const axios = require('axios'); // Axios is a promise-based HTTP client
// Function to fetch weather data from an API
function getWeather(city) {
axios.get(`https://api.weatherapi.com/v1/current.json?key=YOUR_API_KEY&q=${city}`)
.then(response => {
console.log('Weather Data:', response.data);
})
.catch(error => {
console.error('Error fetching weather data:', error);
});
}
getWeather('New York');
In this example, axios.get() returns a Promise, and we use .then() to handle the resolved result (weather data) or .catch() to handle any errors.
While this works, the code can become harder to read and maintain, especially when dealing with multiple asynchronous operations or handling errors across different levels.
Using async and await (Improved Version):
Now, let’s rewrite this example using async and await for better readability:
const axios = require('axios');
// Async function to fetch weather data
async function getWeather(city) {
try {
// Await the response from the API request (async operation)
const response = await axios.get(`https://api.weatherapi.com/v1/current.json?key=YOUR_API_KEY&q=${city}`);
// The code waits here until the above request is complete, then logs the result
console.log('Weather Data:', response.data);
} catch (error) {
// If the request fails, the error is caught here
console.error('Error fetching weather data:', error);
}
}
getWeather('New York');
4. How async and await Work in This Example:
The function getWeather is marked as async. This means it automatically returns a Promise.
Inside the function, await is used to pause the execution until axios.get() resolves the Promise with the weather data.
If the API request is successful, response.data is logged to the console.
If the request fails (for example, if the API is down or the city is invalid), the catch block handles the error.
Key Differences:
Readable Code: With async/await, the asynchronous code looks synchronous, making it much easier to follow and maintain. You don't need to chain .then() and .catch() blocks.
Error Handling: With async/await, errors are caught using try...catch blocks, which is cleaner and more intuitive than using .catch() for promises.
No Nested Callbacks: You avoid the "callback hell" or deeply nested callbacks that can occur when dealing with multiple asynchronous tasks.
5. Real-Time Scenario: Fetching Data from Multiple APIs
Imagine you need to fetch data from two different APIs and combine the results. Using async and await makes it easy to do this without getting lost in nested callbacks or promise chains.
const axios = require('axios');
// Async function to fetch data from multiple APIs
async function getCombinedData(city) {
try {
// Fetching weather data from one API
const weatherResponse = await axios.get(`https://api.weatherapi.com/v1/current.json?key=YOUR_API_KEY&q=${city}`);
// Fetching air quality data from another API
const airQualityResponse = await axios.get(`https://api.airqualityapi.com/v1/current.json?key=YOUR_API_KEY&q=${city}`);
// Combine data from both APIs
const combinedData = {
weather: weatherResponse.data,
airQuality: airQualityResponse.data
};
console.log('Combined Data:', combinedData);
} catch (error) {
console.error('Error fetching combined data:', error);
}
}
getCombinedData('New York');
In this case:
The await keyword is used to wait for the weather data and air quality data sequentially.
Both API calls are handled in order, and the results are combined and logged in a single object.
The code is much easier to understand compared to using multiple .then() chains.
6. Why Use async and await?
Cleaner, more readable code: The flow of asynchronous code is easier to follow without nested callbacks or chaining Promises.
Error handling is simpler: Using try...catch for error handling is much more intuitive than chaining .catch() methods.
Improved debugging: It’s easier to debug async code written with async and await because the stack traces are more readable, and you avoid callback hell.
7. Summary:
async marks a function as asynchronous and implicitly returns a Promise.
await pauses the execution of an async function until a Promise resolves (or rejects).
Together, async and await allow you to write asynchronous code in a synchronous style, making it much easier to read, maintain, and debug.
Comments