Creating Promise Chains in JavaScript. Code examples open

Creating Promise Chains in JavaScript. Code examples

Approved. Code works!
This is exactly the working code that is verified by the moderator or site administrators

One advantage of promises is that they allow you to create chains of promises.

So, earlier we looked at using the then() and catch() methods to receive and handle the results and errors of an asynchronous operation. When executed, these methods generate a new Promise object, on which we can also call the then() and catch() methods,
and thus build a chain of promises.

    promise.then(..).then(..).then(..)

The return value from the handler function in the then() method is passed to the next call to the then() method in the chain:

    const helloPromise = new Promise(function (resolve) {
        resolve("Hello");
    })

    const worldPromise = helloPromise.then(function (value) {
        //return new value
        return value + " World";
    });
    const galaxyPromise = worldPromise.then(function (value) {
        // return new value + old
        return value + " from Milcky Way";
    });
    galaxyPromise.then(function (finalValue) {
        // get the final value
        console.log(finalValue);    // Hello World from Milcky Way
    });

Consider step by step:

First, a hello Promise is created:

    const worldPromise = helloPromise.then(function (value) {
        // return new value
        return value + " World";
    });

The handler function takes the string “Hello” as the value of the value parameter and then returns the string “Hello World“.

This string can then be accessed via the then() method of the new promise, which is generated by calling helloPromise.then(), here called worldPromise.

3. Then the then() method is called on the worldPromise in the same way:

    const galaxyPromise = worldPromise.then(function (value) {
        // return new value
        return value + " from Milcky Way";
    });

The handler function takes the string “Hello World” as the value of the value parameter and then returns the string “Hello World from Milcky Way“.

For greater brevity and clarity, we can simplify the chain:

    new Promise(resolve => resolve("Hello"))
        .then(value => value + " World")
        .then(value => value + " from Milcky Way")
        .then(finalValue => console.log(finalValue));

To handle errors, a catch() method is added to the chain at the end, which will also return a Promise object. Consider on simple example:

    function generateNumber(str) {
        return new Promise((resolve, reject) => {
            const parsed = parseInt(str);
            if (isNaN(parsed)) reject("Not a number");
            else resolve(parsed);
        });
    };
    function printNumber(str) {
        generateNumber(str)
            .then(value => console.log(value))
            .catch(error => console.log(error));
    }
    printNumber("rty"); // Not a number
    printNumber("3");   // 3

In this case, the generateNumber() function returns a promise in which we get some value from the outside, we try to convert it into a number. We call this function in the printNumber() function and create a small chain of the then() and catch() methods for the received promise.

The catch() method is not executed if there are no errors.

Handling Errors in the Promise Chain

Now let’s complicate the chain. Let us have several promises executed in the chain at once:

    function generateNumber(str) {
        return new Promise((resolve, reject) => {
            const parsed = parseInt(str);
            if (isNaN(parsed)) reject("Not a number");
            else resolve(parsed);
        });
    };
    function printNumber(str) {
        generateNumber(str)
            .then(value => {
                if (value === 4) throw "Number error";
                return value * value;
            })
            .then(finalValue => console.log(`Result: ${finalValue}`))
            .catch(error => console.error(error));
    }
    printNumber("rty"); // Not a number
    printNumber("3");   // Result: 9
    printNumber("4");   // Number error
    printNumber("5");   // Result: 25

Return Promise from catch

It is worth noting that since catch() returns a Promise object, then you can also continue the chain further:

    function generateNumber(str) {
        return new Promise((resolve, reject) => {
            const parsed = parseInt(str);
            if (isNaN(parsed)) reject("Not a number");
            else resolve(parsed);
        });
    };
    function printNumber(str) {
        generateNumber(str)
            .then(value => value * value)
            .then(value => console.log(`Result: ${value}`))
            .catch(error => console.error(error))
            .then(() => console.log("Work has been done"));
    }
    printNumber("3");
// Result: 9
// Work has been done

Moreover, the then() method after catch() will be called, even if there were no errors and the catch() method itself was not execute

We can even pass some value from the error handler function in catch() and receive it through the subsequent then() method:

    function generateNumber(str) {
        return new Promise((resolve, reject) => {
            const parsed = parseInt(str);
            if (isNaN(parsed)) reject("Not a number");
            else resolve(parsed);
        });
    };
    function printNumber(str) {
        generateNumber(str)
            .then(value => value * value)
            .then(value => console.log(`Result: ${value}`))
            .catch(error => {
                console.log(error);
                return 0;
            })
            .then(value => console.log("Status code:", value));
    }
    printNumber("ert3");    // Not a number
                        // Status code: 0

finally

This method is executed at the end of the promise chain, regardless of whether an error occurred or the promise was successful.

As a parameter, the method takes a function that performs some final processing of the Promise work:

    function generateNumber(str) {
        return new Promise((resolve, reject) => {
            const parsed = parseInt(str);
            if (isNaN(parsed)) reject("Not a number");
            else resolve(parsed);
        });
    };
    function printNumber(str) {
        generateNumber(str)
            .then(value => console.log(value))
            .catch(error => console.log(error))
            .finally(() => console.log("End"));
    }

    printNumber("3");
    printNumber("triuy")
The finally() method returns a Promise object, so we can continue the chain after it.

Here we are accessing the promise returned by the generateNumber function twice. In one case, the string will be successfully converted to a number, in the other, an error will occur.

However, regardless of the absence or presence of errors in in both cases, the finally() method will be executed, which will print the string “End” to the console.

It is worth noting that data can also be passed to the then() method. But this data is not passed from the finally() method, but from the previous then() or catch() method:

    function generateNumber(str) {
        return new Promise((resolve, reject) => {
            const parsed = parseInt(str);
            if (isNaN(parsed)) reject("Not a number");
            else resolve(parsed);
        });
    };
    function printNumber(str) {
        generateNumber(str)
            .then(value => {
                console.log(value);
                return "hello from then";
            })
            .catch(error => {
                console.log(error);
                return "hello from catch";
            })
            .finally(() => {
                console.log("End");
                return "hello from finally";
            })
            .then(message => console.log(message));
    }
    printNumber("3");//3 End hello from then

Promise execution grouping

The Promise.all(), Promise.allSettled(), and Promise.race() functions allow you to group the execution of multiple promises.

Promise.all function

The Promise.all() function returns a single Promise object that concatenates a set of promises:

    const promise1 = new Promise((resolve, reject) => {
        setTimeout(resolve, 1000, "Hello");
    });
	
    const promise2 = new Promise((resolve, reject) => {
        setTimeout(resolve, 500, "World");
    });
    promise1.then(value => console.log(value));  // Hello
    promise2.then(value => console.log(value));  // World

There are two promises defined here. The asynchronous operation of the first promise is executed after 1000 milliseconds, the action of the second. The promise is fulfilled in 500 milliseconds.

Both of these promises are executed independently of each other. Console output:

    World
    Hello

The Promise.all() function allows you to combine several promises and execute them in parallel, but as a whole. The function takes a set of promises as a parameter:

    Promise.all([promise1, promise2, ...promiseN]);

The return result of the function is a new Promise object.

Now let’s modify the previous example by using the Promise.all() function:

    const promise1 = new Promise((resolve, reject) => {
        setTimeout(resolve, 1000, "Hello");
    });

    const promise2 = new Promise((resolve, reject) => {
        setTimeout(resolve, 500, "World");
    });

    Promise.all([promise1, promise2])
        .then(values => {
            const [promise1data, promise2data] = values;
            console.log(promise1data, promise2data);    // Hello World
        });

Now the data of both promises is returned together and available in the then() method as the values array.

The values of all promises are only returned if they all succeed. But if an error occurs in the asynchronous operation of at least one promise due to internal logic or due to a call to the reject() function, then all promises will go to rejected state, respectively.

Promise.allSettled Function

The function – Promise.allSettled() just like Promise.all() accepts a set of promises and executes them as a whole, but returns an object with the status and result of the promise:

    const promise1 = new Promise((resolve, reject) => {
        reject("Error");
        setTimeout(resolve, 500, "Hello");
    });

    const promise2 = new Promise((resolve, reject) => {
        setTimeout(resolve, 1000, "World");
    });

    Promise.allSettled([promise1, promise2])
        .then(values => {
            const [promise1data, promise2data] = values;
            console.log(promise1data);  // {status: "rejected", reason: "Unexpected error"}
            console.log(promise2data);  // {status: "fulfilled", value: "World"}
        });

In this case, if an error occurs in one of the promises (as in the case of the first promise above), the function also passes the results to the then() method, which follows further in the chain.

Each result represents an object. The first property of this object, status, describes the status or state of the promise. If an error occurs, the status is rejected and the second property represents the error object. If the promise has successfully completed execution, then the status is fulfilled, and the second property – value stores the result of the promise.

Promise.race

The Promise.race() function also accepts multiple promises, only returning the first completed promise (regardless of success or failure):

    const promise1 = new Promise((resolve) => {
        setTimeout(resolve, 500, "Hello");
    });

    const promise2 = new Promise((resolve) => {
        setTimeout(resolve, 1000, "World");
    });

    Promise.race([promise1, promise2])
        .then(value => console.log(value))       // Hello
        .catch(error => console.log(error));

In this case, the promise1 will be the first to be executed. Therefore, the string “Hello” will be passed as value to the then(value => console.log(value)) method.

Promise.any

The Promise.any() function accepts multiple promises and returns the first successful promise:

    const promise1 = new Promise((resolve, reject) => {
        reject("error in promise1");
        setTimeout(resolve, 500, "Hello");
    });
    const promise2 = new Promise((resolve) => {
        setTimeout(resolve, 1000, "World");
    });
    Promise.any([promise1, promise2])
        .then(value => console.log(value))       // World
        .catch(error => console.log(error));

If all promises fail, then an AggregateError type exception is thrown.

Using the errors property of the AggregateError type, you can get all the errors that occurred in the promises as an array:

    const promise1 = new Promise((resolve, reject) => {
        reject("error in promise1");
        setTimeout(resolve, 500, "Hello");
    });

    const promise2 = new Promise((resolve, reject) => {
        reject("error in promise2");
        setTimeout(resolve, 1000, "World");
    });

    Promise.any([promise1, promise2])
        .then(value => console.log(value))
        .catch(e => console.log(e.errors));  // ["error in promise1", "error in promise2"]
0

More

Leave a Reply

Your email address will not be published. Required fields are marked *

How many?: 22 + 22

lil-code© | 2022 - 2024
Go Top
Authorization
*
*
Registration
*
*
*
*
Password generation