Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object literal (hash) with Promise.all

I have a situation where it would be quite convenient to use Promise.all like so Promise.all({}) instead of the more standard Promise.all([]).

but this doesn't seem to work

Promise.all({a:1,b:2}).then(function(val){
   console.log('val:',val);
});

whilst this does of course

Promise.all([1,2,3]).then(function(val){
   console.log('val:',val);
});

(what I would expect would be for Promise.all to map the values of the Object literal, but leave the keys intact.)

But the MDN docs for Promise seem to indicate that Promise all will work for any iterable. To my knowledge, an object literal {} is an iterable. So what am I missing?

like image 343
Alexander Mills Avatar asked Mar 07 '16 00:03

Alexander Mills


3 Answers

Here is another async / await ES6 solution:

async function allOf(hash = {}) {
  const promises = Object.keys(hash).map(async key => ({[key]: await hash[key]}));
  const resolved = await Promise.all(promises);
  return resolved.reduce((hash, part) => ({...hash, ...part}), {});
}

This converts the keys into a promise that produces a single element hash. Then at the end we combine all the hashes in the array to a single hash. You could compact this to a one-liner even, at the cost of readability.

async function allOfOneLiner(hash = {}) {
  return (await Promise.all(Object.keys(hash).map(async k => ({[k]: await hash[k]})))).reduce((h, p) => ({...h, ...p}), {});
}
like image 135
Staale Avatar answered Oct 08 '22 15:10

Staale


Object does not have an Iterator symbol if you look at the mdn documentation for those.

What you can do, is use a tool function to create an object iterable and later consume it.

reference to objectEntries source, however nodejs does not implement Reflect, so for the purpose of using it with node I just change it into using Object.keys()

function objectEntries(obj) {
    let index = 0;

    // In ES6, you can use strings or symbols as property keys,
    // Reflect.ownKeys() retrieves both
    let propKeys = Object.keys(obj);

    return {
        [Symbol.iterator]() {
            return this;
        },
        next() {
            if (index < propKeys.length) {
                let key = propKeys[index];
                index++;
                return { value: [key, obj[key]] };
            } else {
                return { done: true };
            }
        }
    };
}
like image 38
Dmitry Matveev Avatar answered Oct 08 '22 13:10

Dmitry Matveev


Use Object.values. Works in Firefox Nightly:

Promise.all(Object.values({a:1,b:2}))
.then(vals => console.log('vals: ' + vals)) // vals: 1,2
.catch(e => console.log(e));

var console = { log: msg => div.innerHTML += msg + "<br>" };
<div id="div"></div>

Then, to put the results back in an object, we can make a Promise.allParams function:

Promise.allParams = o => 
  Promise.all(Object.values(o)).then(promises =>
    Object.keys(o).reduce((o2, key, i) => (o2[key] = promises[i], o2), {}));

// Demo:

Promise.allParams({a:1,b:2}).then(function(val){
   console.log('val: ' + JSON.stringify(val)); // val: {"a":1,"b":2}
});

var console = { log: msg => div.innerHTML += msg + "<br>" };
<div id="div"></div>
like image 31
jib Avatar answered Oct 08 '22 14:10

jib