wake-up-neo.com

Saubere Art und Weise, auf die erste von Promise zurückgegebene Wahrheit zu warten

Ich arbeite gerade an etwas, bei dem ich drei Versprechen in einem Array abfeuere. Im Moment sieht es so aus

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

Nun werden all diese Versprechen entweder wahr oder falsch zurückkehren. Aber im Moment warte ich darauf, dass alle fertig sind und ich könnte weitermachen, sobald einer von ihnen wahr wird.

Ich dachte an Möglichkeiten, dies zu erreichen, aber alle wirken irgendwie hässlich. Wie würden Sie diese Aufgabe lösen?

21
relief.melone

Sie können diese Kombination aus Promise.race und Promise.all implementieren:

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

Test auf obigen Code:

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

10
jaboja

Sie können ein neues Versprechen erstellen, das aufgelöst wird, sobald ein gegebenes Versprechen in true wie folgt aufgelöst wird:

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()]);

Es verhält sich ähnlich wie Promise.race , löst aber nur auf, wenn eines der angegebenen Versprechen mit dem Wert true aufgelöst wird, anstatt aufgelöst zu werden, sobald eines der angegebenen Versprechen entweder auflöst oder ablehnt.

12
str

Sie möchten grundsätzlich some() .

  • Promise.all() funktioniert nicht, weil Sie warten müssen, bis alle Versprechen abgeschlossen sind.
  • Promise.race() alleine funktioniert nicht, da nur die Auflösung von 1 Promise zurückgegeben wird.

Stattdessen müssen wir ein Array von Versprechen erhalten und so lange weiterlaufen, bis einer von ihnen true zurückkommt. An diesem Punkt sollten wir aufhören. Wenn keiner von ihnen wahr ist, müssen wir noch aufhören, aber wir müssen beachten, dass keiner von ihnen wahr war.

Betrachten Sie das folgende Beispiel mit dem Testkabelbaum:

Code

/**
 * 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());

Ausgabe

// 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
7
zero298

Kann Promise.race () verwenden und anfängliche Versprechen nur dann lösen, wenn der Wert wahr ist

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

7
charlietfl

Wenn ich von strs Antwort abriege, möchte ich die Möglichkeit hinzufügen, einen Rückruf bereitzustellen, so dass dies auf mehr als true/false Promises funktioniert.

Durch das Hinzufügen eines Rückrufs können Sie das Ergebnis eines Versprechens testen und das Versprechen zurückgeben, das erfolgreich zum Rückruffilter passt (der einen true/false-Wert zurückgeben soll).

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
Get Off My Lawn

OK, ich lasse die akzeptierte Antwort so wie sie ist. Aber ich habe es ein bisschen für meine Bedürfnisse geändert, da ich dachte, dass es etwas einfacher zu lesen und zu verstehen ist

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);
        });   
    });    
} 
0
relief.melone