2016-07-27 4 views
-1

Gegeben ein Array mit Verweisen auf asynchrone Funktionen (die Versprechungen oder einfache Werte zurückgeben), ich möchte nacheinander durchlaufen es jedes Element, bis at mindestens eines der Elemente wird in den Truthy-Wert aufgelöst.Sequenziell iterieren Liste von asynchronen Funktionen, bis mindestens eine truthy Wert in Node.js zurückgibt

Die Idee kam von diesem einfachen Synchroncode:

var hasAccess = (user.isAdmin() || user.isManager() || entity.isOwnedBy(user)); 

Der obigen Code gut für synchrone Funktionen arbeitet, wird aber für die asynchron diejenigen brechen. Ich suche nach einem asynchronen Ersatz des obigen Codes.

Ich erwarte, dass es so genannt werden (in ES6 Syntax der Kürze halber):

atLeastOneTruthy([ 
() => user.isAdmin(),  // Called first 
() => user.isManager(),  // Called second if first returned falsy value 
() => entity.isOwnedBy(user) // Called third if all previous returned falsy values 
]).then(function (result) { 
    // result === (true || false) 
}).catch(function() { 
    // Promise chain is rejected when at least one element in rejected 
}); 

alle Funktionen im Beispiel oben Rückkehr entweder Versprechungen oder einfache Werte direkt.

Gibt es eine Out-of-the-Box-Lösung dafür? Ich verwende Bluebird-Versprechen, jedoch konnte ich nichts passendes in seiner API finden. Ich kann z.B. Promise.any(), weil es nur nach gelösten Versprechen sucht und die zurückgegebenen Werte nicht überprüft. Ich kann Promise.map() entweder nicht verwenden, weil es alle Funktionen ausführt, aber ich möchte, dass sie nur bei Bedarf ausgeführt werden (d. H. Wenn vorheriges Element fehlgeschlagen ist).

Eine Node-Modul-Empfehlung wäre großartig (ich konnte keine finden), aber ein Konzept, wie atLeastOneTruthy() codiert werden könnte, wäre auch schön, weil ich einige Zweifel über seine Umsetzung habe.

Antwort

2

Da ein Handler then ein Versprechen zurückgeben kann, können Sie einfach eine rekursive Lösung erstellen.

function atLeastOneTruthy(arrayOfCallables) { 
    // Keep a numeric index to avoid mutating the input array. 
    // This implementation is not safe for item removal, but appending is OK. 
    let nextCallableIndex = 0; 
    // Start with a false value as a base case. Use a "named function expression" 
    // to allow for recursion. 
    return Promise.resolve(false).then(function innerHandler(returnValue) { 
    if (returnValue) { 
     // If truthy, return immediately. Nothing else is evaluated. 
     return returnValue; 
    } else if (nextCallableIndex < arrayOfCallables.length) { 
     // If falsy, get the next iterable... 
     let nextCallable = arrayOfCallables[nextCallableIndex++]; 
     // ...then call it and try again, regardless of whether nextCallable 
     // returns a simple value or a promise. 
     return Promise.resolve(nextCallable()).then(innerHandler); 
    } else { 
     // We're out of callables, and the last one returned a falsy value. 
     // Return false. (You could also return returnValue to parallel ||.) 
     return false; 
    } 
    }); 
} 

Siehe auch:Named function expressions. Sie könnten auch eine einfache verschachtelte Funktion umgestalten.

+1

@zerkms Der Funktionsausdruck 'innerHandler' verläuft, sich in eine' then' Ausdruck. Dies ist nicht rekursiv in dem Sinne, dass die Funktion mehrere Male auf dem Stapel erscheint, aber in dem Sinne rekursiv ist, dass ihre Ausführung selbstreferentiell ist. –

+0

Oh, tatsächlich habe ich das übersehen. – zerkms

+0

Eine sehr elegante Lösung! Ich habe gerade meine "rekursive" Implementierung abgeschlossen, aber ich mag Ihre Lösung besser. Danke, dass du dir Zeit genommen hast! ;) –

0

Vielleicht können Sie eine neue Promise-Methode wie Promise.any() erfinden, die eine Reihe von Versprechungen akzeptiert und löst, wenn einer von ihnen verrechnet und nur dann zurückgewiesen wird, wenn keiner verrechnet wird.

var one = _ => new Promise((res,rej) => setTimeout(rej.bind(null,"boom"),1000)), 
 
    two = _ => new Promise((res,rej) => setTimeout(rej.bind(null,"crash"),1500)), 
 
    three = _ => new Promise((res,rej) => setTimeout(res.bind(null,"yay..!"),500)); 
 

 
Promise.prototype.constructor.any = function(proms){ 
 
    return proms.length ? proms[0].then(v => v, r => Promise.any(proms.slice(1))) 
 
         : Promise.reject("none resolved"); 
 
}; 
 

 
Promise.any([one(),two(),three()]).then(v => console.log("From promise.any with love",v), r => console.log(r)); 
 
Promise.all([one(),two(),three()]).then(v => console.log(v), r => console.log("From Promise.all with love",r));

Verwandte Themen