Promise.all 的对象字面量(散列)

Object literal (hash) with Promise.all

我遇到这样一种情况,使用 Promise.all 会很方便 Promise.all({}) 而不是更标准的 Promise.all([]).

但这似乎不起作用

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

当然可以

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

(我期望 Promise.all 映射对象文字的值,但保持键不变。)

但是 the MDN docs for Promise 似乎表明 Promise all 将适用于任何可迭代对象。据我所知,对象文字 {} 是可迭代的。那我错过了什么?

Syntax
Promise.all(iterable);
Parameters

iterable

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

如果您查看 mdn documentation 对象没有 Iterator 符号。

你可以做的是使用工具函数创建一个可迭代的对象,然后使用它。

reference to objectEntries source,但是 nodejs 没有实现 Reflect,所以为了在 node 中使用它,我只是将其更改为使用 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 };
            }
        }
    };
}

默认情况下并非所有对象都是可迭代的。您可以通过定义 @@iterator 方法使对象可迭代。 @@iterator 是一个 Well-Known Symbol 可用 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.

例如,这将使所有对象都可迭代(可能不是一个好主意):

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

那你就可以使用

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

使用Object.values。在 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>

然后,为了将结果放回一个对象中,我们可以创建一个 Promise.allParams 函数:

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>

对于 Babel/ES2015,您可以使用 Object.keys 和映射来获取这样的值:

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

这个函数可以解决问题:

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

ES6 方式

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;
};

这是另一个异步/等待 ES6 解决方案:

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

这会将键转换为生成单个元素哈希的承诺。然后最后我们将数组中的所有散列组合成一个散列。您甚至可以将其压缩为一行,但要以可读性为代价。

async function allOfOneLiner(hash = {}) {
  return (await Promise.all(Object.keys(hash).map(async k => ({[k]: await hash[k]})))).reduce((h, p) => ({...h, ...p}), {});
}