'filling' an object with async values

608 views Asked by At

I have an object with multiple properties and a few of them contain promises. Now i want to wait until every promise is resolved and then the properties should contain the value of the promise

This is what i have so far, unfortunately the object o still contains the fullfilled promises on the properties o3, o4, o5:

var f_o_data = function(b_reject){

    return new Promise(
        function(
            f_resolve, 
            f_reject
        ){

            window.setTimeout(function(){

                var n_ts_ms = parseInt(new Date().getTime());
                // if(n_ts_ms % 2 == 0){
                //     f_reject(`i rejected because n_ts_ms ${n_ts_ms} % 2 == 0 is true :shrug:`)
                // }
                if(b_reject){
                    f_reject('i rejected because true was passed to function')
                }
                f_resolve(
                    {
                        n_ts_ms: n_ts_ms,
                        s: 'i am the result object', 
                        n: 23, 
                        b: true, 
                        a_n: [1,2,3], 
                        a_o:[{n:2, n:3}, {n:3}]
                    }
                )

            },parseInt(Math.random()*3333+555))
        }
    )
}
var f_o__filled_asyncally = async function(){

    var o = {
        b: true, 
        n: 2, 
        s: 'just a string', 
        o: {n:22}, 
        a_n: [1,2,3],
        a_o:[{n:2, n:3}, {n:3}],
        o1: await f_o_data(),// those two first async functions will be executed one after another because we use await keyword
        o2: await f_o_data(),// 
        o3: f_o_data(),// theese three can load parallel
        o4: f_o_data().then(function(o){return o}),// theese three can load parallel
        o5: f_o_data(),// theese three can load parallel
        // o6: f_o_data(true),// theese three can load parallel
    }
    return Promise.all(Object.values(o)).then(
        function(a_o_promise){
            return o
        }
    )
    // console.log(o)
    // console.log(o.o3)
    // console.log(o.o4)
    // console.log(o.o5)
    // return o;
}

var o_my_object_i_want_to_fill_asyncally = await f_o__filled_asyncally();
console.log(o_my_object_i_want_to_fill_asyncally)
2

There are 2 answers

0
T.J. Crowder On

There are at least two answers to this question:

  1. An answer for f_o__filled_asyncally,specifically, making it return a promise for an object that will have all the values as non-promises.

  2. A general answer to the question in the title: Going from an object with some properties that are promises to an object with all non-promise values. But because of the sequential nature of one aspect of the code in the question, the general answer won't quite apply to that sample code.

The f_o__filled_asyncally-specific answer

Wait to build your object until you have all of the information:

const f_o__filled_asyncally = async function () {
    // Do the work first
    const [
        [o1, o2], 
        o3,
        o4,
        o5
    ] = await Promise.all([
        // All outer elements work in parallel
        (async () => [
            await f_o_data(), // These two are sequential
            await f_o_data(),
        ])(),
        f_o_data(),
        f_o_data().then(function (o) {
            // There's no point to this `then` call, but
            // I've left it in case you had something
            // in it that you didn't show
            return o;
        }),
        f_o_data(),
        // f_o_data(true), // Not sure why `o6` is commented out
    ]);

    // Build and return the object
    const result = {
        b: true,
        n: 2,
        s: "just a string",
        o: { n: 22 },
        a_n: [1, 2, 3],
        a_o: [{ n: 2, n: 3 }, { n: 3 }],
        o1,
        o2,
        o3,
        o4,
        o5,
        // o6,
    };
    return result;
};

You might want to split out the o1 and o2 part more explicitly:

const f_o__filled_asyncally = async function () {
    // Do the work first
    const promiseForO1AndO2 = (async () => [
        await f_o_data(), // These two are sequential
        await f_o_data(),
    ])();
    const [
        [o1, o2], 
        o3,
        o4,
        o5
    ] = await Promise.all([
        // All outer elements work in parallel
        promiseForO1AndO2,
        f_o_data(),
        f_o_data().then(function (o) {
            // There's no point to this `then` call, but
            // I've left it in case you had something
            // in it that you didn't show
            return o;
        }),
        f_o_data(),
        // f_o_data(true), // Not sure why `o6` is commented out
    ]);

    // Build and return the object
    const result = {
        b: true,
        n: 2,
        s: "just a string",
        o: { n: 22 },
        a_n: [1, 2, 3],
        a_o: [{ n: 2, n: 3 }, { n: 3 }],
        o1,
        o2,
        o3,
        o4,
        o5,
        // o6,
    };
    return result;
};

The general answer

This probably doesn't quite fit your scenario because you have those two calls that have to be sequential, but it fits the title of the question:

async function awaitAllProperties(obj) {
    const entries = await Promise.all(Object.entries(obj).map(
        async ([key, value]) => [key, await value]
    ));
    return Object.fromEntries(entries);
}

That will accept an object, wait for the promises (if any) on any of its properties to be settled, and return a new object with the fulfillment values.

Again, because you have a pair of operations you've said you want done sequentially, it wouldn't quite apply to the example code though.

0
Jonas Frey On

i think i will go with this

var f_o__filled_asyncally = async function(o){

    var a_s_prop_name = Object.keys(o);
    return Promise.all(Object.values(o)).then(
        function(a_value){
            var a_a_v = a_s_prop_name.map((s_prop_name, n_idx) => [s_prop_name, a_value[n_idx]]);
            return Object.fromEntries(a_a_v)
        }
    )
}

var o = {
    b: true, 
    n: 2, 
    s: 'just a string', 
    o: {n:22}, 
    a_n: [1,2,3],
    a_o:[{n:2, n:3}, {n:3}],
    o1: await f_o_data(),// those two first async functions will be executed one after another because we use await keyword
    o2: await f_o_data(),// 
    o3: f_o_data(),// theese three can load parallel
    o4: f_o_data().then(function(o){return o}),// theese three can load parallel
    o5: f_o_data(),// theese three can load parallel
    // o6: f_o_data(true),// theese three can load parallel
}
var o_my_object_i_want_to_fill_asyncally = await f_o__filled_asyncally(o);
console.log(o_my_object_i_want_to_fill_asyncally)