使用Promise.all获得基于名称的结果的最佳方法

时间:2016-02-09 16:42:17

标签: javascript es6-promise

默认情况下,Promise.All([])函数返回一个基于数字的索引数组,其中包含每个promise的结果。

var promises = [];
promises.push(myFuncAsync1()); //returns 1
promises.push(myFuncAsync1()); //returns 2
Promise.all(promises).then((results)=>{
    //results = [0,1]
}

使用Promise.all()返回指定结果索引的最佳方法是什么?

我尝试使用Map,但它以这种方式返回数组中的结果: [key1, value1, key2, value2]

更新

我的问题似乎不清楚,这就是为什么我不喜欢基于订购的索引:

  1. 维护很糟糕:如果在代码中添加一个promise,则可能需要重写整个结果函数,因为索引可能已更改。
  2. 读起来很糟糕results[42](可以通过下面的jib答案解决)
  3. 在动态环境中不可用
  4. var promises = [];
    if(...)
        promises.push(...);
    else{
        [...].forEach(... => { 
            if(...)
                promises.push(...);
            else
                [...].forEach(... => {
                    promises.push(...);
                });
        });
    }
    Promise.all(promises).then((resultsArr)=>{
        /*Here i am basically fucked without clear named results 
            that dont rely on promises' ordering in the array */
    });
    

5 个答案:

答案 0 :(得分:47)

ES6支持解构,因此如果您只想为结果命名,可以写:



var myFuncAsync1 = () => Promise.resolve(1);
var myFuncAsync2 = () => Promise.resolve(2);

Promise.all([myFuncAsync1(), myFuncAsync2()])
  .then(([result1, result2]) => console.log(result1 +" and "+ result2)) //1 and 2
  .catch(e => console.error(e));




现在可以在Firefox和Chrome中使用。

答案 1 :(得分:9)

这是什么事吗?

<img>

答案 2 :(得分:3)

如果您不想修改结果对象的格式,这是一个帮助程序功能,该功能允许为每个条目分配名称以供以后访问。

const allNamed = (nameToPromise) => {
    const entries = Object.entries(nameToPromise);
    return Promise.all(entries.map(e => e[1]))
        .then(results => {
            const nameToResult = {};
            for (let i = 0; i < results.length; ++i) {
                const name = entries[i][0];
                nameToResult[name] = results[i];
            }
            return nameToResult;
        });
};

用法:

var lookup = await allNamed({
    rootStatus: fetch('https://stackoverflow.com/').then(rs => rs.status),
    badRouteStatus: fetch('https://stackoverflow.com/badRoute').then(rs => rs.status),
});

var firstResult = lookup.rootStatus; // = 200
var secondResult = lookup.badRouteStatus; // = 404

如果您使用的是打字稿,甚至可以使用keyof构造指定输入键和结果之间的关系:

type ThenArg<T> = T extends PromiseLike<infer U> ? U : T;

export const allNamed = <
    T extends Record<string, Promise<any>>,
    TResolved extends {[P in keyof T]: ThenArg<T[P]>}
>(nameToPromise: T): Promise<TResolved> => {
    const entries = Object.entries(nameToPromise);
    return Promise.all(entries.map(e => e[1]))
        .then(results => {
            const nameToResult: TResolved = <any>{};
            for (let i = 0; i < results.length; ++i) {
                const name: keyof T = entries[i][0];
                nameToResult[name] = results[i];
            }
            return nameToResult;
        });
};

enter image description here

答案 3 :(得分:0)

关于@kragovip的答案,此处显示您希望避免的原因:

https://medium.com/front-end-weekly/async-await-is-not-about-making-asynchronous-code-synchronous-ba5937a0c11e

” ......习惯于等待所有网络和I / O呼叫真的很容易。

但是,由于await关键字停止了其后所有代码的执行,因此在连续多次使用它时应小心。 (与同步代码中的代码完全一样)”

错误示例(请勿关注)

async function processData() {
  const data1 = await downloadFromService1();
  const data2 = await downloadFromService2();
  const data3 = await downloadFromService3();

  ...
}

“绝对没有必要等待第一个请求的完成,因为其他请求都不取决于它的结果。

我们希望并行发送请求,并等待所有请求同时完成。这就是异步事件驱动编程的强大功能所在。

要解决此问题,我们可以使用Promise.all()方法。我们将Promises从异步函数调用中保存到变量中,将它们组合到一个数组中,然后立即等待它们。”

相反

async function processData() {
  const promise1 = downloadFromService1();
  const promise2 = downloadFromService2();
  const promise3 = downloadFromService3();

  const allResults = await Promise.all([promise1, promise2, promise3]);

答案 4 :(得分:-1)

一个很好的解决方案是使用异步等待。并非完全像您要求的ES6,而是ES8!但是由于Babel完全支持它,所以我们开始:

通过使用async / await,可以避免仅使用数组索引,如下所示。

async函数允许您在函数内部使用await关键字,将其放在promise之前,从而实际上停止了其中的代码。当async函数在尚未解决的承诺中遇到await时,该函数会立即返回一个待处理的承诺。函数实际上稍后完成时,此返回的诺言就会解析。该函数仅在解析先前等待的promise时恢复,在此期间它将整个await Promise语句解析为该Promise的返回值,从而允许您将其放入变量中。这有效地使您可以在不阻塞线程的情况下暂停代码。通常,这是处理JavaScript中异步内容的一种好方法,因为它使您的代码更加按时间顺序排列,因此更易于推理:

async function resolvePromiseObject(promiseObject) {
    await Promise.all(Object.values(promiseObject));

    const ret = {};

    for ([key, value] of Object.entries(promiseObject)) {
        // All these resolve instantly due to the previous await
        ret[key] = await value;
    };

    return ret;
}

与ES5之上的任何版本一样:请确保正确配置Babel,以便使用较旧浏览器的用户可以毫无问题地运行您的代码。只要您的babel配置正确,就可以在IE11上使async等待功能完美地工作。