Understanding Promise.allSettled in Node.js: Managing Tasks Explained

🎓
This episode is a part of the Learn JavaScript Promises collection and is #4 episode of them all.

Introduction

Promise.allSettled is a method available in Node.js that allows developers to handle multiple promises in parallel. It returns an array of objects that contain the status and the result of each promise passed to it, regardless of whether the promise is fulfilled or rejected. This can be useful for situations where you need to handle multiple asynchronous operations in parallel, but don't need to handle the results in a specific order.


Step 1: Import the Promise object

In order to use the Promise.allSettled method, you first need to import the Promise object. You can do this by adding the following line of code at the top of your JavaScript file:

const Promise = require('promise');

Step 2: Create an array of promises

Next, you'll need to create an array of promises that you want to handle in parallel. Here's an example of how you might create an array of promises that resolve after a set time interval:

const promise1 = new Promise((resolve) => {
    setTimeout(() => {
        resolve('Promise 1 resolved');
    }, 1000);
});

const promise2 = new Promise((resolve) => {
    setTimeout(() => {
        resolve('Promise 2 resolved');
    }, 2000);
});

const promise3 = new Promise((resolve) => {
    setTimeout(() => {
        resolve('Promise 3 resolved');
    }, 3000);
});

const promiseArray = [promise1, promise2, promise3];

Step 3: Call the Promise.allSettled method

Once you have an array of promises, you can call the Promise.allSettled method and pass in the array as an argument. Here's an example of how you might do this:

Promise.allSettled(promiseArray)
    .then((results) => {
        console.log(results);
    });

The Promise.allSettled method returns an array of objects, each object contains the status of each promise and the result if any. Here's an example of what the results array might look like:

[
    { status: 'fulfilled', value: 'Promise 1 resolved' },
    { status: 'fulfilled', value: 'Promise 2 resolved' },
    { status: 'fulfilled', value: 'Promise 3 resolved' }
]

Step 4: Handle the results

Once you have the results, you can iterate over the array and handle the results as needed. Here's an example of how you might handle the results using a for loop:

Promise.allSettled(promiseArray)
    .then((results) => {
        for (let i = 0; i < results.length; i++) {
            console.log(`Promise ${i + 1} ${results[i].status} with value: ${results[i].value}`);
        }
    });

This will print the following output to the console:

Promise 1 fulfilled with value: Promise 1 resolved
Promise 2 fulfilled with value: Promise 2 resolved
Promise 3 fulfilled with value: Promise 3 resolved

Step 4: Examples of using Promise.allSettled in a Node.js application

  • Example 1: Handling multiple API calls

In this example, let's say you need to make multiple API calls to different endpoints in parallel, and you don't care about the order in which the responses come back. You can use Promise.allSettled to handle the API calls and the responses in parallel.

const axios = require('axios');

const api1 = axios.get('https://jsonplaceholder.typicode.com/todos/1');
const api2 = axios.get('https://jsonplaceholder.typicode.com/todos/2');
const api3 = axios.get('https://jsonplaceholder.typicode.com/todos/3');
const apiArray = [api1, api2, api3];

Promise.allSettled(apiArray)
    .then((results) => {
        results.forEach((result, index) => {
            if (result.status === 'fulfilled') {
                console.log(`API ${index + 1} returned: ${result.value.data}`);
            } else {
                console.log(`API ${index + 1} failed with reason: ${result.reason}`);
            }
        });
    });
  • Example 2: Handling file operations

In this example, let's say you need to read the contents of multiple files in parallel, and you don't care about the order in which the files are read. You can use Promise.allSettled to handle the file operations and the contents in parallel.

const fs = require('fs');

const readFile1 = (file) => {
    return new Promise((resolve, reject) => {
        fs.readFile(file, 'utf8', (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
};
const file1 = readFile1('file1.txt');
const file2 = readFile1('file2.txt');
const file3 = readFile1('file3.txt');
const fileArray = [file1, file2, file3];

Promise.allSettled(fileArray)
    .then((results) => {
        results.forEach((result, index) => {
            if (result.status === 'fulfilled') {
                console.log(`File ${index + 1} content: ${result.value}`);
            } else {
                console.log(`File ${index + 1} failed to read with reason: ${result.reason}`);
            }
        });
    });
  • Example 3: Handling database queries

In this example, let's say you need to run multiple database queries in parallel, and you don't care about the order in which the queries are executed. You can use Promise.allSettled to handle the database operations and the results in parallel.

const mysql = require('mysql2/promise');

const pool = mysql.createPool({
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'mydb',
});

const query1 = pool.query('SELECT * FROM users WHERE id = 1');
const query2 = pool.query('SELECT * FROM users WHERE id = 2');
const query3 = pool.query('SELECT * FROM users WHERE id = 3');
const queryArray = [query1, query2, query3];

Promise.allSettled(queryArray)
    .then((results) => {
        results.forEach((result, index) => {
            if (result.status === 'fulfilled') {
                console.log(`Query ${index + 1} returned: ${result.value[0]}`);
            } else {
                console.log(`Query ${index + 1} failed with reason: ${result.reason}`);
            }
        });
    });
  • Example 4: Handling image processing

In this example, let's say you need to process multiple images in parallel, and you don't care about the order in which the images are processed. You can use Promise.allSettled to handle the image processing operations and the results in parallel.

const Jimp = require('jimp');

const processImage1 = (image) => {
    return new Promise((resolve, reject) => {
        Jimp.read(image)
            .then((image) => {
                image.resize(256, 256)
                    .quality(60)
                    .write(`processed_${image}`);
                resolve(`Image ${image} processed successfully`);
            })
            .catch((err) => {
                reject(err);
            });
    });
};
const image1 = processImage1('image1.jpg');
const image2 = processImage1('image2.jpg');
const image3 = processImage1('image3.jpg');
const imageArray = [image1, image2, image3];

Promise.allSettled(imageArray)
    .then((results) => {
        results.forEach((result, index) => {
            if (result.status === 'fulfilled') {
                console.log(`${result.value}`);
            } else {
                console.log(`Image ${index + 1} failed to process with reason: ${result.reason}`);
            }
        });
    }); 

In these examples, Promise.allSettled allows you to handle multiple database queries and image processing operations in parallel, regardless of whether they succeed or fail. It gives you a way to handle these asynchronous operations in a more robust and resilient way, and handle any errors that may occur during the process.


Conclusion

The Promise.allSettled method in Node.js is a powerful tool for handling multiple asynchronous operations in parallel. By following the step-by-step guide and tips provided in this article, you can learn how to use Promise.allSettled to make your code more robust and resilient. Whether you're making API calls, handling file operations, or running database queries, Promise.allSettled allows you to handle any rejected promises along with the fulfilled ones, giving you more control over how you handle the results. With this knowledge, you can write more efficient and effective code for handling asynchronous operations in Node.js.