web-dev-qa-db-ja.com

プロミスチェーンから適切に抜け出す方法は?

ここの質問に基づいて: jQuery chaining and cascading then's and when's および受け入れられた答え、私はある時点で約束の連鎖を破りたいが、まだ正しい方法を見つけていない。 複数投稿about がありますが、私はまだ迷っています。

元の質問からサンプルコードを取得します。

Menus.getCantinas().then(function(cantinas){ // `then` is how we chain promises
    Menus.cantinas = cantinas;
    // if we need to aggregate more than one promise, we `$.when`
    return $.when(Menus.getMeals(cantinas), Menus.getSides(cantinas));
}).then(function(meals, sides){ // in jQuery `then` can take multiple arguments
    Menus.sides = sides; // we can fill closure arguments here
    Menus.meals = meals;
    return Menus.getAdditives(meals, sides); // again we chain
}).then(function(additives){
    Menus.additives = additives;
    return Menus; // we can also return non promises and chain on them if we want
}).done(function(){ // done terminates a chain generally.
     // edit HTML here
});

cantinas.length == 0の場合、どのようにチェーンを解除しますか?率直に言って、私は食事も添加物も得たくありません。何らかの「空の結果」コールバックを呼び出したいと思います。私は非常にいである以下を試してみました(しかし動作します...)。正しい方法を教えてください。これはまだ有効な結果なので、それ自体は「失敗」ではなく、単に空の結果です。

var emptyResult = false;
Menus.getCantinas().then(function(cantinas){
    Menus.cantinas = cantinas;
    if (cantinas.length == 0) {
      emptyResult = true;
      return "emptyResult"; //unuglify me
    }
    return $.when(Menus.getMeals(cantinas), Menus.getSides(cantinas));
}).then(function(meals, sides){ 
    if (meals == "emptyResult") return meals;  //look at my ugliness...
    Menus.sides = sides;
    Menus.meals = meals;
    return Menus.getAdditives(meals, sides);
}).then(function(additives){
    if (additives == "emptyResult") return additives;
    Menus.additives = additives;
    return Menus;
}).done(function(){
   if (emptyResult)
     //do empty result stuff
   else
     // normal stuff
});
32
Dennis G

第一に、約束の連鎖を「破る」のではなく、「迂回する」(その一部)を求めていると言う方が良いと思います。

あなたが言うように、いくつかの場所で「emptyResult」をテストするのはかなりugいです。幸いなことに、いくつかのプロミスチェーンを実行しないという同じ一般原則を順守しながら、よりエレガントなメカニズムを利用できます。

別のメカニズムは、Promise Rejectionを使用してフローを制御し、チェーンの後半で特定のエラー状態を再検出して、成功パスに戻すことです。

Menus.getCantinas().then(function(cantinas) {
    Menus.cantinas = cantinas;
    if(cantinas.length == 0) {
        return $.Deferred().reject(errMessages.noCantinas);
    } else {
        return $.when(Menus.getMeals(cantinas), Menus.getSides(cantinas));
    }
}).then(function(meals, sides) {
    Menus.sides = sides;
    Menus.meals = meals;
    return Menus.getAdditives(meals, sides);
}).then(function(additives) {
    Menus.additives = additives;
    return Menus;
}).then(null, function(err) {
    //This "catch" exists solely to detect the noCantinas condition 
    //and put the chain back on the success path.
    //Any genuine error will be propagated as such.
    //Note: you will probably want a bit of safety here as err may not be passed and may not be a string.
    return (err == errMessages.noCantinas) ? $.when(Menus) : err;
}).done(function(Menus) {
    // with no cantinas, or with everything
});

var errMessages = {
    'noCantinas': 'no cantinas'
};

プラス面としては、ネスティングの欠如が自然な成功の道をより読みやすくすることだと思います。また、少なくとも私にとって、このパターンは、必要に応じて、さらなるバイパスに対応するための最小限のメンタルジャグリングを必要とします。

マイナス面では、このパターンはベルギのパターンよりもわずかに効率が劣ります。メインパスには、ベルギと同じ数の約束がありますが、cantinas.length == 0パスにはもう1つ(複数のバイパスがコーディングされている場合はバイパスごとに1つ)が必要です。また、このパターンでは、特定のエラー状態(したがってerrMessagesオブジェクト)を確実に再検出する必要があります。

14
Roamer-1888

あなたのような音 branch壊れないようにdone。 promiseの素晴らしい特性は、チェーンだけでなく、制限なしに ネストおよび非ネスト にできることです。あなたの場合、あなたはあなたのif- statementの中に「分割」したいチェーンの部分を置くことができます:

Menus.getCantinas().then(function(cantinas) {
    Menus.cantinas = cantinas;

    if (cantinas.length == 0)
        return Menus; // break!

    // else
    return $.when(Menus.getMeals(cantinas), Menus.getSides(cantinas))
    .then(function(meals, sides) {
        Menus.sides = sides;
        Menus.meals = meals;
        return Menus.getAdditives(meals, sides);
    }).then(function(additives) {
        Menus.additives = additives;
        return Menus;
    });
}).done(function(Menus) {
    // with no cantinas, or with everything
});
20
Bergi

組み込みのブラウザPromiseを使用し、拒否ケースをすべての消費者に知らせずに、チェーンthen 'sまたはcatchesをトリガーするか、Uncaught (in promise)エラー、次を使用できます。

var noopPromise = {
  then: () => noopPromise, 
  catch: () => noopPromise
}

function haltPromiseChain(promise) {
  promise.catch(noop)

  return noopPromise
}

// Use it thus:
var p = Promise.reject("some error")
p = haltPromiseChain(p)
p.catch(e => console.log(e)) // this never happens

基本的に、noopPromiseは基本的なスタブアウトされたpromiseインターフェースであり、チェーン機能を使用しますが、決して実行しません。これは、ブラウザが明らかにダックタイピングを使用して何かが約束かどうかを判断するという事実に依存しているため、YMMV(Chrome 57.0.2987.98でこれをテストしました)ですが、それが問題になる場合おそらく実際のpromiseインスタンスを作成し、そのthenを削除してメソッドをキャッチできます。

9
jstaab