干净的方法来等待Promise返回的第一个true

时间:2018-07-03 17:40:44

标签: javascript node.js promise

我目前正在做一些我要在数组中实现三个承诺的事情。目前看起来像这样

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

现在所有这些承诺都会返回true或false。但是目前我正在等待所有这些操作完成,只要其中一个返回true,我就可以继续。

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

6 个答案:

答案 0 :(得分:12)

您可以创建一个new promise,一旦给定的诺言解析为print(df) MultiIndex(levels=[['bar', 'baz', 'foo', 'qux'], ['one', 'two']], labels=[[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]], names=['first', 'second']) ,它就会立即解决:

true

它的行为类似于Promise.race,但仅在给定的一个promise解析为值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解析或拒绝时立即解决。

答案 1 :(得分:12)

您可以结合使用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]);

答案 2 :(得分:7)

可以使用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);
  
});

答案 3 :(得分:7)

您基本上想要some()

  • Promise.all()无法正常工作,因为它将使您等待所有承诺完成。
  • Promise.race()单独行不通,因为它只返回1 Promise的分辨率。

相反,我们需要接收一个Promises数组,并继续进行竞赛,直到其中一个返回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})));
        // 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());
        });
    }
]

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

答案 4 :(得分:5)

超越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));

答案 5 :(得分:0)

好的,我将保留接受的答案。但是我根据自己的需要对它做了一些修改,因为我认为此书更易于阅读和理解

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