获得第一个兑现的承诺

Posted

技术标签:

【中文标题】获得第一个兑现的承诺【英文标题】:Get first fulfilled promise 【发布时间】:2017-02-17 19:19:43 【问题描述】:

如果我有两个 Promise A 和 B,其中只有一个会成功,我怎样才能得到哪个成功实现?我正在寻找类似于Promise.race 的东西,但它只会返回第一个实现的承诺。我正在使用来自 ES6 的 Promise。

【问题讨论】:

您正在寻找Promise.any (Bluebird, Creed, WinJS, ...) 【参考方案1】:

ES2021 / ES12 - Promise.any

​​>

Promise.any - 第一个履行承诺获胜。

const promiseA = Promise.reject();
const promiseB = new Promise((resolve) => setTimeout(resolve, 100, 'succeed'));

const promises = [promiseA, promiseB];


Promise.race(promises).then((value) => console.log(value)); // rejected promise

Promise.any(promises).then((value) => console.log(value)); // "succeed"

请注意,any 忽略了第一个被拒绝的承诺 - promiseA,因为正在解决 promiseB

如果所有给定的 Promise 都被拒绝,则返回的 Promise 被拒绝。


这是finished proposal,计划在 ES2021(预计 2021 年 6 月发布)

【讨论】:

【参考方案2】:

我有同样的问题并试一试。通过自己尝试这些问题,您会学到很多东西!

    接受的答案非常优雅,但使用了Promise.all,这对学习 Promises 的人来说很有趣;也有点难以遵循 imo。 jfriend00 的答案与我的相似,但其逻辑超出了 Promises 的基础知识,这在此处最为重要。

我已经使用了已接受答案中的那些不错的辅助函数:

function firstPromise(promiseL, promiseR) 
    return new Promise((resolve, reject) => 
        promiseL.then(l => 
            resolve(l);
        ).catch(error => null);

        promiseR.then(r => 
            resolve(r);
        ).catch(error => null);


        promiseL.catch(errorL => 
            promiseR.catch(errorR => 
                reject(errorL + errorR);
            )
        )
    )


const wait = ms => new Promise(res => setTimeout(() => res(ms), ms));
const log = p => p.then(v => console.log("pass", v), v => console.log("fail", v));


log(firstPromise(wait(1000), wait(500)));
log(firstPromise(wait(1000), Promise.reject("Bar")));
log(firstPromise( Promise.reject("Foo"), wait(500)));
log(firstPromise( Promise.reject("Foo"), Promise.reject("Bar")));

【讨论】:

【参考方案3】:

如果你想要第一个成功解决的承诺,并且你想忽略之前的任何拒绝,那么你可以使用这样的东西:

// returns the result from the first promise that resolves
// or rejects if all the promises reject - then return array of rejected errors
function firstPromiseResolve(array) 
    return new Promise(function(resolve, reject) 
        if (!array || !array.length) 
            return reject(new Error("array passed to firstPromiseResolve() cannot be empty"));
        
        var errors = new Array(array.length);
        var errorCntr = 0;
        array.forEach(function (p, index) 
            // when a promise resolves
            Promise.resolve(p).then(function(val) 
                // only first one to call resolve will actually do anything
                resolve(val);
            , function(err) 
                errors[index] = err;
                ++errorCntr;
                // if all promises have rejected, then reject
                if (errorCntr === array.length) 
                    reject(errors);
                
            );
        );
    );

我不明白你如何使用Promise.race(),因为它只是报告第一个要完成的承诺,如果第一个承诺被拒绝,它会报告拒绝。因此,报告第一个解决的承诺并没有按照您在问题中提出的要求(即使在它之前完成了一些拒绝)。

仅供参考,the Bluebird promise library 有 Promise.some()Promise.any() 可以为您处理这种情况。

【讨论】:

您应该使用p.then(…, …) 而不是p.then(…); p.catch(…) 以避免未处理的拒绝 @Bergi - 哪里有未处理的拒绝? 如果p 拒绝,p.then(…) 的结果是一个被拒绝且永远不会处理的承诺。 我说的是结果。在q = p.then(…); p.catch(…) 中,p 的拒绝被处理(两次),是的,但q 的拒绝从未被处理,因此全球报告,这是错误的。 ES7 指定了未处理的拒绝是如何工作的,并且它们确实错误地使用您的原始代码触发。你的书关于.then(…).catch(…) 是正确的,但它说一般地,这是我们想要.then(…, …) 的特定情况(我们从不想要fn1 @987654336 @ 被称为两者) - 这并不是完全错误的,因为您的 fn1 永远不会抛出,但它更清洁,更高效,并且如果您在 fn1 中犯了错误(如拼写错误),它会正确报告。【参考方案4】:
 //example 1
    var promise_A = new Promise(function(resolve, reject) 
        // выполнить что-то, возможно, асинхронно…
        setTimeout(function()

            return  resolve(10);
            //return reject(new Error('ошибка'))
        ,10000)
    );

    var promise_B = new Promise(function(resolve, reject) 
        // выполнить что-то, возможно, асинхронно…

        setTimeout(function()
            return  resolve(100);
        ,2000)
    );


/*
 //[100,10]
 Promise.all([
  promise_A,promise_B
 ]).then(function(results)
  console.log(results)
 );
*/

 //100
 Promise.race([
  promise_A,promise_B
 ]).then(function(results)
  console.log(results)
 );

【讨论】:

这将返回解决或拒绝的承诺,而不是第一个履行的承诺。 西里尔文的 cmets 是怎么回事?【参考方案5】:

反转promise的极性,然后你可以使用Promise.all,因为它在第一个被拒绝的promise上reject,反转后对应于第一个实现的promise:

const invert  = p  => new Promise((res, rej) => p.then(rej, res));
const firstOf = ps => invert(Promise.all(ps.map(invert)));

// Utility routines used only in testing.
const wait    = ms => new Promise(res => setTimeout(() => res(ms), ms));
const fail    = f  => Promise.reject(f);
const log     = p  => p.then(v => console.log("pass", v), v => console.log("fail", v));

// Test.
log(firstOf([wait(1000), wait(500) ]));
log(firstOf([wait(1000), fail("f1")]));
log(firstOf([fail("f1"), fail("f2")]));

这将返回第一个履行的承诺的值,或者如果全部拒绝,则返回一系列拒绝原因。

【讨论】:

聪明。这不是第一次反转概念在承诺领域中发挥作用。 @jfriend00 是的,这种技术至少出现在 SO 上的另一个答案中,我无法追踪,不是原创的。 这是一个巧妙的技巧。 @torazaburo,你可能见过this answer 也可以写成Promise.prototype.invert()Promise.any() - jsfiddle.net/akvewbje

以上是关于获得第一个兑现的承诺的主要内容,如果未能解决你的问题,请参考以下文章

汇道当初的承诺兑现了吗?

coding!8月31日 来挖文兑现承诺

Java培训机构承诺的java工程师包就业兑现了吗

我的第一个python web开发框架(21)——小结

电子书 | BSV区块链:它是什么?它如何兑现企业级区块链的承诺?

电子书 | BSV区块链:它是什么?它如何兑现企业级区块链的承诺?