11

ich ein Versprechen, in einer solchen Art und Weise zu brechen,wie Versprechen Kette

function getMode(){ 
    var deferred = Promise.defer(); 

    checkIf('A') 
    .then(function(bool){ 
     if(bool){ 
      deferred.resolve('A'); 
     }else{ 
      return checkIf('B'); 
     } 
    }).then(function(bool){ 
     if(bool){ 
      deferred.resolve('B'); 
     }else{ 
      return checkIf('C'); 
     } 
    }).then(function(bool){ 
     if(bool){ 
      deferred.resolve('C'); 
     }else{ 
      deferred.reject(); 
     } 
    }); 

    return deferred.promise; 
} 

checkIf gibt ein Versprechen, und ja checkIfnicht modifiziert werden.

Wie kann ich beim ersten Match aus der Kette ausbrechen? (eine andere Möglichkeit als explizit Fehler werfen?)

+1

Haben Sie diese Antwort geprüft? http://stackoverflow.com/questions/20714460/break-promise-chain-and-call-a-function-based-on-the-step-in-the-chain-where-it#answer-20715224 – Mario

+0

@Mario , ja, hat das überprüft, aber meins ist nicht kaputt, ich will bewusst aus der Versprechenskette ausbrechen ... – mido

+1

eine Wahl, die Sie haben, ist http://jsfiddle.net/arunpjohny/1vc7crhw/1/ –

Antwort

3

Ich denke, dass Sie hier keine Kette wollen. In einer synchronen Art und Weise, hätte man

geschrieben
function getMode(){ 
    if (checkIf('A')) { 
     return 'A'; 
    } else { 
     if (checkIf('B')) { 
      return 'B'; 
     } else { 
      if (checkIf('C')) { 
       return 'C'; 
      } else { 
       throw new Error(); 
      } 
     } 
    } 
} 

und dies ist, wie sollte es verspricht übersetzt werden:

function getMode(){ 
    checkIf('A').then(function(bool) { 
     if (bool) 
      return 'A'; 
     return checkIf('B').then(function(bool) { 
      if (bool) 
       return 'B'; 
      return checkIf('C').then(function(bool) { 
       if (bool) 
        return 'C'; 
       throw new Error(); 
      }); 
     }); 
    }); 
} 

Es gibt keine if else -flattening in Versprechungen.

+2

einer der auti-Muster: dann enthält dann –

+0

@KenBerkeley: 'dann' beinhaltet' else' beinhaltet 'then'. Bitte schlagen Sie vor, wie man es flach macht? Und nein, "dann" in einem "dann" ist absolut kein Versprechen Antipattern. Sie dürfen nicht vergessen, das Versprechen (wie immer) zurückzugeben. – Bergi

+0

@Bergi, ist das kein Kandidat für Ihre klassische '.Catch()' Kette? –

1

Sie könnten eine firstSucceeding-Funktion erstellen, die entweder den Wert der ersten erfolgreichen Operation zurückgeben oder eine NonSucceedingError werfen würde.

Ich habe ES6 Versprechungen verwendet, aber Sie können den Algorithmus anpassen, um die Promise-Schnittstelle Ihrer Wahl zu unterstützen.

function checkIf(val) { 
 
    console.log('checkIf called with', val); 
 
    return new Promise(function (resolve, reject) { 
 
     setTimeout(resolve.bind(null, [val, val === 'B']), 0); 
 
    }); 
 
} 
 

 
var firstSucceeding = (function() { 
 
    
 
    return function (alternatives, succeeded) { 
 
     var failedPromise = Promise.reject(NoneSucceededError()); 
 
     return (alternatives || []).reduce(function (promise, alternative) { 
 
      return promise.then(function (result) { 
 
        if (succeeded(result)) return result; 
 
        else return alternative(); 
 
       }, alternative); 
 
     }, failedPromise).then(function (result) { 
 
      if (!succeeded(result)) throw NoneSucceededError(); 
 
      return result; 
 
     }); 
 
    } 
 
    
 
    function NoneSucceededError() { 
 
     var error = new Error('None succeeded'); 
 
     error.name = 'NoneSucceededError'; 
 
     return error; 
 
    } 
 
})(); 
 

 
function getMode() { 
 
    return firstSucceeding([ 
 
     checkIf.bind(null, 'A'), 
 
     checkIf.bind(null, 'B'), 
 
     checkIf.bind(null, 'C') 
 
    ], function (result) { 
 
     return result[1] === true; 
 
    }); 
 
} 
 

 
getMode().then(function (result) { 
 
    console.log('res', result); 
 
}, function (err) { console.log('err', err); });

+0

@ downvoter, Würde es Ihnen etwas ausmachen zu sagen, was mit dem Ansatz falsch ist? – plalx

+0

Nicht downvoter - aber es verwendet das verzögerte Anti-Pattern ohne Grund - es ist ein wenig sinnlos, umgehängt zu umgehen und 'Promise.defer()' hier zu verwenden. –

+0

@BenjaminGruenbaum Könntest du das etwas erweitern? Wie würden Sie zulassen, dass "getMode" ein Versprechen zurückgibt, das nur gelöst wird, wenn eine der vielen asynchronen Alternativen die nachfolgende Bedingung erfüllt, ohne ein zusätzliches Versprechen zu geben? – plalx

3

Ich würde coroutines/spawns nur verwenden, führt dies zu viel einfacher Code:

function* getMode(){ 
    if(yield checkIf('A')) 
     return 'A'; 
    if(yield checkIf('B')) 
     return 'B'; 
    if(yield checkIf('C')) 
     return 'C'; 
    throw undefined; // don't actually throw or reject with non `Error`s in production 
} 

Wenn Sie Generatoren haben nicht dann gibt es traceur oder 6to5 immer.

+0

Warum werfen Sie 'null' o_0? –

+0

Ich kann nicht warten, bis alle gängigen Browser Generatoren unterstützen. – plalx

+0

@BenjaminGruenbaum OP abgelehnt mit 'undefined'. – simonzack

10

Jeder andere Weg als explizit werfen Fehler?

Möglicherweise müssen Sie etwas werfen, aber es muss kein Fehler sein.

meisten Versprechen Implementierungen haben Methode catch akzeptieren das erste Argument als Fehlertyp (aber nicht alle, und nicht ES6 Versprechen), wäre es in dieser Situation hilfreich sein:

function BreakSignal() { } 

getPromise() 
    .then(function() { 
     throw new BreakSignal(); 
    }) 
    .then(function() { 
     // Something to skip. 
    }) 
    .catch(BreakSignal, function() { }) 
    .then(function() { 
     // Continue with other works. 
    }); 

ich hinzufügen, die Fähigkeit zu brechen die kürzliche Implementierung meiner eigenen Versprechens-Bibliothek. Und wenn Sie ThenFail verwendet haben (wie würden Sie wahrscheinlich nicht), können Sie so etwas schreiben kann:

getPromise() 
    .then(function() { 
     Promise.break; 
    }) 
    .then(function() { 
     // Something to skip. 
    }) 
    .enclose() 
    .then(function() { 
     // Continue with other works. 
    }); 
0

Ich mag viele der bisher geschrieben Antworten, die zu mildern, was die q readme die „Pyramide des Schicksals“ nennt . um der diskussion willen, füge ich das muster hinzu, das ich ausgepackt habe, bevor ich mich umschaut, um zu sehen, was andere tun.Ich schrieb eine Funktion wie

var null_wrap = function (fn) { 
    return function() { 
    var i; 
    for (i = 0; i < arguments.length; i += 1) { 
     if (arguments[i] === null) { 
     return null; 
     } 
    } 
    return fn.apply(null, arguments); 
    }; 
}; 

und ich tat etwas völlig analog zu @ vilicvane Antwort, mit der Ausnahme, anstatt throw new BreakSignal(), ich return null geschrieben hatte und wickelte alle nachfolgenden .then Rückrufe in null_wrap wie

then(null_wrap(function (res) { /* do things */ })) 

Ich denke, das ist eine gute Antwort b/c es vermeidet viel Eindruck und b/c das OP speziell nach einer Lösung gefragt, die nicht throw. Das heißt, ich kann zurückgehen und etwas mehr verwenden wie @vilicvane b/c einige Versprechen der Bibliothek könnte null zurück, um etwas anderes als "die Kette zu brechen", und das könnte verwirrend sein.

Dies ist mehr ein Aufruf für mehr Kommentare/Antworten als ein "das ist definitiv der Weg, es zu tun" Antwort.

0

Wahrscheinlich hier spät, um die Partei kommen, aber ich gab vor kurzem eine Antwort mit Generatoren und die co Bibliothek, die diese Frage beantworten würde (Lösung 2 sehen):

Der Code würde etwas wie:

const requestHandler = function*() { 

     const survey = yield Survey.findOne({ 
      _id: "bananasId" 
     }); 

     if (survey !== null) { 
      console.log("use HTTP PUT instead!"); 
      return; 
     } 

     try { 
      //saving empty object for demonstration purposes 
      yield(new Survey({}).save()); 
      console.log("Saved Successfully !"); 
      return; 
     } 
     catch (error) { 
      console.log(`Failed to save with error: ${error}`); 
      return; 
     } 

    }; 

    co(requestHandler) 
     .then(() => { 
      console.log("finished!"); 
     }) 
     .catch(console.log); 

Sie würden ziemlich viel synchronen Code schreiben, der wäre in Wirklichkeit asynchron!

Hoffe es hilft!

3

können Sie verwenden return { then: function() {} };

.then(function(bool){ 
    if(bool){ 
     deferred.resolve('A'); 
     return { then: function() {} }; // end/break the chain 
    }else{ 
     return checkIf('B'); 
    } 
}) 

Die return-Anweisung a gibt "dann-able", nur dass die dann Methode nichts tut. Wenn then() von einer Funktion in then() zurückgegeben wird, versucht then(), das Ergebnis vom thenable zu erhalten. Der Then-fähige "then" nimmt einen Rückruf, der aber in diesem Fall nie aufgerufen wird. So kommt das "then()" zurück und der Callback für den Rest der Kette findet nicht statt.

Verwandte Themen