最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Object literal (hash) with Promise.all - Stack Overflow

programmeradmin3浏览0评论

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?

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?

Share Improve this question edited Mar 7, 2016 at 4:30 user663031 asked Mar 7, 2016 at 0:55 Alexander MillsAlexander Mills 100k165 gold badges531 silver badges908 bronze badges 6
  • 3 No, {} is not an iterable. – user663031 Commented Mar 7, 2016 at 1:19
  • It's not clear from these docs whether {} is an iterable or not, but it appears it is developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… – Alexander Mills Commented Mar 7, 2016 at 1:23
  • @AlexMills Not sure why you think it's not clear. The MDN article you linked explains "some built-in types are built-in iterables with a default iteration behavior, such as Array or Map, while other types (such as Object) are not". – Oriol Commented Mar 7, 2016 at 2:38
  • If you are wondering whether {} is an iterable or not, try typing any of the following into the console. (1) [...{}] (2) for (i of {}) {}. – user663031 Commented Mar 7, 2016 at 3:43
  • 2 Why would it really be convenient anyway? You're not explaining what you're trying to do. There are a lot of possible things you could be trying to do, but none of them really make sense for just cramming an object into Promise.all, whose purpose is to merge Promises, which doesn't look like what your'e trying to do. – Dtipson Commented Mar 7, 2016 at 21:39
 |  Show 1 more comment

8 Answers 8

Reset to default 5

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}), {});
}

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>

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 };
            }
        }
    };
}
Syntax
Promise.all(iterable);
Parameters

iterable

An iterable object, such as an Array. See iterable.

This function does the trick:

Promise.allAssoc = function(object){
    var values = [], keys = [];
    for(var key in object){
        values.push(object[key]);
        keys.push(key);
    }
    return Promise.all(values).then(function(results){
        var out = {};
        for(var i=0; i<results.length; i++) out[keys[i]] = results[i];
        return out;
    });
};

Not all objects are iterable by default. You can make an object iterable by defining a @@iterator method. @@iterator is a Well-Known Symbol available as Symbol.iterator:

  • Specification Name
    @@iterator
  • [[Description]]
    "Symbol.iterator"
  • Value and Purpose
    A method that returns the default Iterator for an object. Called by the semantics of the for-of statement.

For example, this will make all object iterable (probably not a good idea):

Object.prototype[Symbol.iterator] = function*() {
  for(let key of Object.keys(this))
    yield this[key];
};

Then you will be able to use

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

With Babel/ES2015 you can use Object.keys and map to get the values like this:

const obj = {a:1,b:2};                                                                                                                               
const vals = Object.keys(obj).map(k=>obj[k]);                                                                                                        
Promise.all(vals).then( vals => { console.log('vals', vals) });                         

ES6 way

Promise.hashProperties = async function(object) {
    const keys = [];
    const values = [];

    for (const key in object) {
        keys.push(key);
        values.push(object[key]);
    }

    const results = await Promise.all(values);

    for (var i=0; i<results.length; i++)
        object[keys[i]] = results[i];

    return object;
};
发布评论

评论列表(0)

  1. 暂无评论