Node.js
async/await
HTTP requests
asynchronous programming
JavaScript

Proper request with async/await in Node.JS

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Understanding Asynchronous Requests with async/await in Node.js

Asynchronous programming is an essential part of Node.js, allowing it to handle operations without blocking the main application thread. Among the various techniques for handling asynchronous code, async/await provides a clean, readable, and straightforward syntax built on top of Promises.

In this article, we'll delve through the intricacies of making proper requests using async/await in Node.js, explore various related topics, and illustrate with examples.

What is async/await?

async/await is a syntactic sugar over Promises, introduced in ECMAScript 2017 (ES8), that allows writing asynchronous code in a synchronous-like manner. With this construct, you can avoid the "callback hell" and write cleaner code.

  • async Function: An async function is a function declared with the async keyword. It automatically returns a Promise. This promise resolves with the value returned by the function or rejects with a thrown error.
  • await Expression: The await keyword can only be used within an async function. It makes JavaScript pause execution until a Promise is fulfilled, after which execution resumes with the resolved value of the promise.

Making HTTP Requests

To demonstrate async/await, let's consider making HTTP requests, a common asynchronous operation. For this, we'll use the node-fetch library, a lightweight module that simulates window.fetch from the browser in Node.js.

First, install node-fetch:

bash
npm install node-fetch

Example: Fetching Data from an API

javascript
1const fetch = require('node-fetch');
2
3async function fetchData(url) {
4    try {
5        const response = await fetch(url);
6        
7        // Check if the request was successful
8        if (!response.ok) {
9            throw new Error(`Network response was not ok: ${response.statusText}`);
10        }
11        
12        // Parse and return JSON data
13        const data = await response.json();
14        return data;
15    } catch (error) {
16        console.error('Failed to fetch data:', error);
17        throw error;
18    }
19}
20
21(async () => {
22    const url = 'https://jsonplaceholder.typicode.com/posts/1';
23    try {
24        const data = await fetchData(url);
25        console.log(data);
26    } catch (error) {
27        console.error('Error in fetching the post data:', error);
28    }
29})();

Key Elements of the Example

  1. Async Function Usage: The fetchData function is declared with the async keyword, returning a promise.
  2. Error Handling: By using try-catch, errors during the fetch or parsing process can be caught and managed.
  3. Response Validation: Check the response status using response.ok to ensure that the HTTP request was successful.
  4. Fetching JSON Data: The await keyword pauses the function execution until the response is received and then parsed.

Benefits of Using async/await

  • Readability: Code appears synchronous, making it easier to understand and maintain.
  • Error Handling: With try-catch, handling errors is seamless compared to chaining .catch() handlers.
  • Less Boilerplate: Reduces nesting often seen in promise chaining.

Limitations

  • Cannot Be Used in Top-Level Code: await needs to be within an async function.
  • Not Suitable for Non-Promise Operations: async/await does not make synchronous functions asynchronous.

Advanced Usage

Concurrent Requests

When dealing with multiple requests, you can perform concurrency using Promise.all.

javascript
1async function fetchMultipleData(urls) {
2    try {
3        const fetchPromises = urls.map(url => fetch(url).then(res => res.json()));
4        const data = await Promise.all(fetchPromises);
5        return data;
6    } catch (error) {
7        console.error('Failed to fetch multiple data:', error);
8        throw error;
9    }
10}
11
12(async () => {
13    const urls = [
14        'https://jsonplaceholder.typicode.com/posts/1',
15        'https://jsonplaceholder.typicode.com/posts/2',
16        'https://jsonplaceholder.typicode.com/posts/3'
17    ];
18    try {
19        const data = await fetchMultipleData(urls);
20        console.log(data);
21    } catch (error) {
22        console.error('Error in fetching multiple data:', error);
23    }
24})();

Conclusion

async/await streamlines handling asynchronous operations in Node.js by offering a syntax that is intuitive and less error-prone compared to traditional methods. By understanding and applying this construct properly, developers can significantly enhance the readability and maintainability of their code.

Summary Table

FeatureDescription
async FunctionDeclares a function that returns a Promise. Automatically wraps non-promise values within a resolved Promise.
await KeywordPauses the function execution until a Promise is settled. Returns the resolved value from the Promise.
Error HandlingUtilizes try-catch blocks for effective error management.
ConcurrencyAchieved using Promise.all to handle multiple Promises concurrently.
LimitationsCannot be used at the top level without an async context and unsuitable for non-promise or synchronous operations.

By integrating async/await into your Node.js applications, you can create robust and efficient software, capable of managing asynchronous tasks gracefully.


Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.