Search code examples
javascriptnode.jsasync-awaites6-promise

Node async/await with promise.all


In a simple JS program, I need to have asyncOperation2 and asyncOperation3 execute sequentially along with asyncOperation1. Meaning I need the following to happen:

1) order of execution as  1,2,3 or 2,3,1
2) then after step 1. I need to preform an operation on the result of 1,2,3

Here's what I have so far(this will run in Node). But I cannot get the order of operations to happen as I described above.

const App = {
    total: 0,

    init: function () {
        // I want to run all 3 operations. (with 2 and 3 happening sequentially)
        Promise.all([App.asyncOperation1(1000), App.combine2and3()])
            .then((total) => {
                // then I want to use the result of all 3 operations
                console.log(`total from top: ${total}`);
            })
    },

    asyncOperation1: function (time) {
        return new Promise(function (resolve, reject) {
            var intervalID = setTimeout(function () {
                resolve(time);
            }, time);
        });
    },

    asyncOperation2: async function (time) {
        setTimeout(function () {
            return time;
        }, time);
    },

    asyncOperation3: async function (time) {
        setTimeout(function () {
            return time;
        }, time);
    },

    combine2and3: async function () {
        var value2 = await App.asyncOperation2(2000);
        var value3 = await App.asyncOperation3(1000);
        console.log(`value2: ${value2}`)
        console.log(`value3: ${value3}`)
        console.log(`summed total ${value2 + value3}`);
        return value2 + value3;
    },
};

App.init();

Actual results:

value2: undefined
value3: undefined
summed total NaN
total from top: 1000,NaN

Desired results:

value2: 2000
value3: 1000
summed total 3000
total from top: 1000,3000

Solution

  • The problem is that asyncOperation2() and asyncOperation3() return a promise (because of their async declaration) that has no resolved value (thus it's undefined) because those functions don't have their own return value.

    asyncOperation2: async function (time) {
        setTimeout(function () {
            return time;     // this just returns back into the timer sub-system
        }, time);
        // there is no return value for your function here
        // thus the promise the async function returns has an undefined resolved value
    },
    

    Returning from inside the setTimeout() isn't the function's return value. That's just a return back into the timer innards that happens long after the function itself has returned. Remember, setTimeout() is non-blocking. It schedules some code to run sometime in the future and then returns immediately and your function then finishes and returns (before setTimeout() has fired). So, then sometime later, your setTimeout() fires and you return a value from that callback and that value just goes back into the timer sub-system, not to any of your code or to any promise.

    Change those two functions to this:

    function delay(t, v) {
       return new Promise(resolve => {
           setTimeout(resolve.bind(null, v));
       }, t);
    }
    
    asyncOperation2: function (time) {
        return delay(time, time);
    },
    
    asyncOperation3: function (time) {
        return delay(time, time);
    },
    

    Now you have functions that return a promise that is resolved with the desired value. Note, there's no need for them to be declared async either since you aren't using await inside the functions and you're already creating your own promise to return.