Async Programming with JavaScript: Part 2

 

Async Programming with JavaScript: Part 2

Handling Multiple Asynchronous Operations

In Part 1 of this series, we covered the basics of asynchronous programming, including callbacks, promises, and the modern async/await syntax. Now, in Part 2, we’re going to take things further by exploring how to handle multiple asynchronous operations. In real-world applications, it’s common to perform several tasks at once — for example, fetching data from multiple APIs, processing files in parallel, or running multiple background tasks concurrently. We'll cover:

  • Promise.all()
  • Promise.race()
  • Handling multiple asynchronous operations with async/await

Let’s get started!


1. Promise.all(): Running Promises in Parallel

The Promise.all() method is used to run multiple asynchronous operations in parallel. It takes an array of promises as input and returns a single promise that resolves when all of the input promises are fulfilled or rejects as soon as one of the promises rejects.

Syntax

javascript
Promise.all([promise1, promise2, promise3]) .then((results) => { // Handle all results as an array }) .catch((error) => { // Handle error if any promise is rejected });

Example of Promise.all()

Imagine you’re fetching data from two APIs concurrently, and you want to wait for both of them to complete before continuing.

javascript
async function fetchData() { const userData = fetch('https://jsonplaceholder.typicode.com/users'); const postData = fetch('https://jsonplaceholder.typicode.com/posts'); try { const [users, posts] = await Promise.all([userData, postData]); const usersJson = await users.json(); const postsJson = await posts.json(); console.log('Users:', usersJson); console.log('Posts:', postsJson); } catch (error) { console.error('Error fetching data:', error); } } fetchData();

How it works:

  • Promise.all() takes an array of promises and resolves when all of them are fulfilled.
  • We use await to get the results of the two API calls and extract their JSON data.
  • If any of the promises reject, the .catch() block catches the error, ensuring proper error handling.

Key Points:

  • Parallel execution: Promise.all() allows promises to run concurrently, reducing wait time.
  • If any one promise fails, the entire Promise.all() call is rejected.
  • The result of Promise.all() is an array containing the results of all the resolved promises in the same order.

2. Promise.race(): First Promise to Settle Wins

While Promise.all() waits for all promises to be fulfilled, Promise.race() resolves or rejects as soon as one of the promises resolves or rejects. This is useful when you want to know which operation finishes first.

Syntax

javascript
Promise.race([promise1, promise2, promise3]) .then((result) => { // Handle the first settled promise }) .catch((error) => { // Handle error if the first promise rejects });

Example of Promise.race()

javascript
const timeout = new Promise((_, reject) => setTimeout(() => reject('Timeout!'), 3000)); const fetchData = fetch('https://jsonplaceholder.typicode.com/posts'); Promise.race([timeout, fetchData]) .then((response) => { console.log('First to settle:', response); }) .catch((error) => { console.log('Error:', error); });

How it works:

  • Promise.race() will settle as soon as either the timeout or fetchData promise settles (whichever happens first).
  • In this case, if the fetch takes more than 3 seconds, the timeout promise will reject first, and we handle that in the .catch() block.
  • Promise.race() is useful when you need a timeout mechanism or the first completed task to be processed immediately.

Key Points:

  • First settled: Promise.race() resolves when the first promise settles, either by resolving or rejecting.
  • Great for scenarios like timeouts, competitive processes, or when you want to prioritize the quickest response.

3. Handling Multiple Asynchronous Operations with Async/Await

In the previous parts of this series, we introduced async/await, which greatly simplifies asynchronous code by making it look more like synchronous code. But what happens when you need to handle multiple asynchronous tasks?

With async/await, you can use Promise.all() or Promise.race() in combination with await to handle multiple asynchronous operations.

Example: Parallel Async/Await with Promise.all()

javascript
async function fetchData() { const userResponse = fetch('https://jsonplaceholder.typicode.com/users'); const postResponse = fetch('https://jsonplaceholder.typicode.com/posts'); try { const [userData, postData] = await Promise.all([userResponse, postResponse]); const users = await userData.json(); const posts = await postData.json(); console.log('Users:', users); console.log('Posts:', posts); } catch (error) { console.error('Error fetching data:', error); } } fetchData();

How it works:

  • Just like before, we run both fetch calls in parallel.
  • Promise.all() is used to wait for both promises to resolve simultaneously.
  • We then parse the JSON data from both responses after they’re both complete.

Example: Handling First to Settle with Promise.race()

javascript
async function fetchWithTimeout() { const timeout = new Promise((_, reject) => setTimeout(() => reject('Timeout!'), 2000)); const fetchRequest = fetch('https://jsonplaceholder.typicode.com/posts'); try { const response = await Promise.race([timeout, fetchRequest]); const data = await response.json(); console.log('Fetched data:', data); } catch (error) { console.log('Error:', error); // This handles both timeout or fetch errors } } fetchWithTimeout();

How it works:

  • We use Promise.race() to handle a timeout. If the fetch request takes longer than 2 seconds, the timeout promise will reject first, and the error will be handled by the catch block.

Key Points:

  • You can use async/await with Promise.all() and Promise.race() to manage multiple asynchronous operations.
  • Promise.all() handles tasks in parallel, while Promise.race() resolves/rejects as soon as the first promise settles.

4. Handling Errors with Multiple Async Operations

Error handling becomes even more critical when dealing with multiple asynchronous operations. Consider these scenarios:

  • What happens if one operation fails while others succeed?
  • How do you handle errors in both Promise.all() and Promise.race()?

Error Handling in Promise.all()

In Promise.all(), if any promise fails, the entire promise will be rejected, and you can catch the error using a catch() block.

javascript
async function fetchData() { const userData = fetch('https://jsonplaceholder.typicode.com/users'); const postData = fetch('https://jsonplaceholder.typicode.com/posts'); try { const [users, posts] = await Promise.all([userData, postData]); const usersJson = await users.json(); const postsJson = await posts.json(); console.log(usersJson, postsJson); } catch (error) { console.error('Error fetching data:', error); // Handles error if any promise fails } }

Error Handling in Promise.race()

In Promise.race(), if any promise fails (i.e., rejects first), the error will be caught immediately.

javascript
const timeout = new Promise((_, reject) => setTimeout(() => reject('Timeout!'), 2000)); const fetchData = fetch('https://jsonplaceholder.typicode.com/posts'); Promise.race([timeout, fetchData]) .then((result) => { console.log('First to settle:', result); }) .catch((error) => { console.error('Error:', error); // Handles error from either promise });

Key Points:

  • Always handle errors in Promise.all() and Promise.race() using .catch() or try/catch (in async functions).
  • In Promise.all(), all promises must succeed, or the whole operation fails.
  • In Promise.race(), the first promise to settle determines the result.

Conclusion

In Part 2, we explored how to handle multiple asynchronous operations in JavaScript using Promise.all() and Promise.race(). These tools allow you to manage multiple promises concurrently, making it

Post a Comment

0 Comments