干净的方式等待承诺返回的第一个真实



我目前正在做一些事情,我在数组中发出三个承诺。目前它看起来像这样

var a = await Promise.all([Promise1(), Promise2(), Promise3()]);

现在,所有这些承诺要么返回真,要么返回假。但目前我正在等待所有这些完成,一旦其中一个返回真,我就可以继续。

我想到了实现这一目标的方法,但一切似乎都有些丑陋。您将如何解决此任务?

您可以实现Promise.racePromise.all的组合:

function firstTrue(promises) {
const newPromises = promises.map(p => new Promise(
(resolve, reject) => p.then(v => v && resolve(true), reject)
));
newPromises.push(Promise.all(promises).then(() => false));
return Promise.race(newPromises);
}

测试上述代码:

function firstTrue(promises) {
const newPromises = promises.map(p => new Promise(
(resolve, reject) => p.then(v => v && resolve(true), reject)
));
newPromises.push(Promise.all(promises).then(() => false));
return Promise.race(newPromises);
}
var test = values => firstTrue(
values.map((v) => new Promise((resolve) => {
setTimeout(() => resolve(v), Math.round(Math.random() * 1000));
}))
).then((ret) => console.log(values, ret));
test([true, true, true]);
test([false, false, false]);
test([true, false, false]);
test([false, true, false]);
test([false, false, true]);

您可以创建一个新承诺,一旦任何给定的承诺解析为true,就会像这样解析:

function promiseRaceTrue(promises) {
return new Promise(function(resolve, reject) {
promises.forEach(promise =>
promise.then(val => val === true && resolve())
// TODO handle resolve with value of "false"?
// TODO handle rejection?
);
// TODO handle all resolved as "false"?
});
}
var a = await promiseRaceTrue([Promise1(), Promise2(), Promise3()]);

它的行为类似于Promise.race但仅当其中一个给定承诺使用值true解析时才解析,而不是在任何给定承诺解析或拒绝后立即解析。

如果你想要最先进的解决方案,听起来你想要Promise.any()

Promise.any()接受Promise对象的迭代对象,并且只要 可迭代实现中的一个承诺,返回单个承诺 这可以通过该承诺的价值来解决。如果 可迭代实现(如果所有给定的承诺都被拒绝(,则 返回的承诺被拒绝,并带有一个新的子类AggregateError将各个错误组合在一起Error。从本质上讲,这 方法与Promise.all()相反。

但是,这要等到

可以使用
  • 铬 85
  • 火狐 79
  • 野生动物园 14
  • 不支持 IE、Edge 或 Opera

你基本上想要some().

  • Promise.all()不起作用,因为它会让你等待所有的承诺完成。
  • Promise.race()不起作用,因为它只返回 1 个承诺的分辨率。

相反,我们需要收到一系列承诺并继续比赛,直到其中一个返回true此时,我们应该停止。 如果它们都不是真的,我们仍然需要停止,但我们需要注意它们都不是真的。

请考虑以下示例和测试工具:

法典

/**
* Promise aware setTimeout based on Angulars method
* @param {Number} delay How long to wait before resolving
* @returns {Promise} A resolved Promise to signal the timeout is complete
*/
function $timeout(delay) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), delay);
});
}
/**
* Return true (early) if any of the provided Promises are true
* @param {Function(arg: *): Boolean} predicate The test the resolved Promise value must pass to be considered true
* @param {Promise[]} arr The Promises to wait on
* @returns {Promise<Boolean>} Whether one of the the provided Promises passed the predicate
*/
async function some(predicate, arr) {
// Don't mutate arguemnts
const arrCopy = arr.slice(0);
// Wait until we run out of Promises
while(arrCopy.length){
// Give all our promises IDs so that we can remove them when they are done
const arrWithIDs = arrCopy.map((p, idx) => p.then(data => ({idx, data})).catch(_err => ({idx, data: false})));
// Wait for one of the Promises to resolve
const soon = await Promise.race(arrWithIDs);
// If it passes the test, we're done
if(predicate(soon.data))return true;
// Otherwise, remove that Promise and race again
arrCopy.splice(soon.idx, 1);
}
// No Promises passed the test
return false;
}
// Test harness
const tests = [
function allTrue(){
console.log(new Date());
return some((v)=>v, [
$timeout(1000).then(() => true),
$timeout(2000).then(() => true),
$timeout(3000).then(() => true)
]).then(d => {
console.log(d);
console.log(new Date());
});
},
function twoSecondsTrue(){
console.log(new Date());
return some((v)=>v, [
$timeout(1000).then(() => false),
$timeout(2000).then(() => true),
$timeout(3000).then(() => true)
]).then(d => {
console.log(d);
console.log(new Date());
});
},
function threeSecondsTrue(){
console.log(new Date());
return some((v)=>v, [
$timeout(1000).then(() => false),
$timeout(2000).then(() => false),
$timeout(3000).then(() => true)
]).then(d => {
console.log(d);
console.log(new Date());
});
},
function allFalse(){
console.log(new Date());
return some((v)=>v, [
$timeout(1000).then(() => false),
$timeout(2000).then(() => false),
$timeout(3000).then(() => false)
]).then(d => {
console.log(d);
console.log(new Date());
});
},
function threeSecondsTrueWithError(){
console.log(new Date());
return some((v)=>v, [
$timeout(1000).then(() => { throw new Error() }),
$timeout(2000).then(() => false),
$timeout(3000).then(() => true)
]).then(d => {
console.log(d);
console.log(new Date());
});
}
]
tests.reduce((acc, curr) => acc.then(()=>curr()), Promise.resolve());

输出

// 1 Second true
2018-07-03T18:41:33.264Z
true
2018-07-03T18:41:34.272Z
// 2 Seconds true
2018-07-03T18:41:34.273Z
true
2018-07-03T18:41:36.274Z
// 3 Seconds true
2018-07-03T18:41:36.274Z
true
2018-07-03T18:41:39.277Z
// 3 Seconds false
2018-07-03T18:41:39.277Z
false
2018-07-03T18:41:42.282Z
// 3 Seconds true with error throwing
2018-07-03T18:41:42.282Z
true
2018-07-03T18:41:45.285Z

可以使用 Promise.race(( 并且仅在值为 true 时才解析初始承诺

const doSomething = (bool, val)=>{
return new Promise((resolve, reject)=>{
if (bool){
resolve(val)
}
})
}
const promise1 = doSomething(false,'one')
const promise2 = doSomething(false,'two')
const promise3 = doSomething(true,'three')
const promise4 = doSomething(true,'four')
const promise5 = doSomething(true,'five')
Promise.race([promise1, promise2, promise3, promise4, promise5]).then(value => {
console.log('First true one to resolve is: ', value);

});

从str的答案出发,我想添加提供回调的功能,因此这适用于true/false多个承诺。

添加回调将允许测试任何 Promise 的结果,并返回成功匹配回调过滤器的承诺(应返回true/false值(。

Promise.until = function(callback, ...promises) {
return new Promise(function(resolve, reject) {
promises.forEach(promise =>
promise.then(val => callback(val) === true && resolve(val))
)
})
}
// Create some functions that resolve true/false
function Promise1() {return new Promise(resolve => setTimeout(()=> resolve(false), 1000))}
function Promise2() {return new Promise(resolve => setTimeout(()=> resolve(true), 3000))}
function Promise3() {return new Promise(resolve => setTimeout(()=> resolve(false), 1000))}
// Create som functions that resolve objects
function Promise4() {return new Promise(resolve => setTimeout(()=> resolve({a:1}), 1000))}
function Promise5() {return new Promise(resolve => setTimeout(()=> resolve({a:2}), 3000))}
function Promise6() {return new Promise(resolve => setTimeout(()=> resolve({a:123}), 1000))}
// Create some functions that resolve strings
function Promise7() {return new Promise(resolve => setTimeout(()=> resolve('Brass'), 1000))}
function Promise8() {return new Promise(resolve => setTimeout(()=> resolve('Monkey'), 500))}
function Promise9() {return new Promise(resolve => setTimeout(()=> resolve(['Brass', 'Monkey']), 100))}
// Once one resolves `true` we will catch it
Promise.until(result => result === true, Promise1(), Promise2(), Promise3())
.then(result => console.log(result));
// Once one resolves where `a` equals 123, we will catch it
Promise.until(result => result.a === 123, Promise4(), Promise5(), Promise6())
.then(result => console.log(result));

// Once one resolves where `a` equals 123, we will catch it
Promise.until(result => result === 'Monkey', Promise7(), Promise8(), Promise9())
.then(result => console.log(result));

好的,我将保留接受的答案。但是我根据自己的需要对其进行了一点更改,因为我认为这个更容易阅读和理解

const firstTrue = Promises => {
return new Promise((resolve, reject) => {
// map each promise. if one resolves to true resolve the returned promise immidately with true
Promises.map(p => {
p.then(result => {
if(result === true){
resolve(true);
return;
} 
});
});
// If all promises are resolved and none of it resolved as true, resolve the returned promise with false
Promise.all(Promises).then(() => {
resolve(Promises.indexOf(true) !== -1);
});   
});    
} 

最新更新