2012-07-02 5 views
34

私は多くのステップがあるシナリオで "雲のピラミッド"を避けるためにNode.jsにthe Q moduleを使用しています。例:Qを使用してnode.js promise chainを正しく終了するにはどうすればよいですか?

function doTask(task, callback) 
{ 
    Q.ncall(task.step1, task) 
    .then(function(result1){ 
     return Q.ncall(task.step2, task); 
    }) 
    .then(function(result2){ 
     return Q.ncall(task.step3, task); 
    }) 
    .fail(callback).end(); 
} 

これは基本的には動作しているようです。いずれかのタスクステップによってエラーがスローされた場合、そのエラーはコールバックに渡されます(ただし、node.js約束を初めて熟知しているので、改善を歓迎します)。しかし、タスクチェーンを早期に中止する必要があるときには問題があります。例えば、結果1が正常に返された場合、私は早くコールバックを呼び出して、残りを中止する場合がありますが、そうする私の試みは失敗している...この例では

function doTask(task, callback) 
{ 
    Q.ncall(task.step1, task) 
    .then(function(result1){ 
     if(result1) 
     {// the rest of the task chain is unnecessary 
      console.log('aborting!'); 
      callback(null, result1); 
      return null; 
     } 
     return Q.ncall(task.step2, task); 
    }) 
    .then(function(result2){ 
     console.log('doing step 3...'); 
     return Q.ncall(task.step3, task); 
    }) 
    .fail(callback).end(); 
} 

、私は両方とも「中止!」を参照してください。 「ステップ3を実行中...」と表示されます。

私はここでいくつかの基本原則を誤解していると思いますので、助けていただければ幸いです。ありがとう!

+0

一つの解決策は、最初の鎖が壊れるかもしれませんした後、別の約束チェーンを作成することです)(の.doneに変更されました。上の例より、result2の.thenステートメントは、元の約束にアタッチされるのではなく、step2のQ.ncallにアタッチされます。しかし、ここでの主な欠点は、私の意見ではQの主な利点の1つを取り除くことです:運命のピラミッドを避ける!それはまったく何の約束よりも良いですが、私は解決策が嫌いです... –

答えて

16

約束チェーンの中にスローされたエラーは、スタック全体が早期に中止されなければ、制御はエラーバックパスに与える原因になります。 (このケースでは、fail()ハンドラ)あなたがpromise chainを中止したい特定の状態を検出すると、非常に具体的なエラーをスローします。エラーバックにトラップして無視します選択してください)

function doTask(task, callback) 
{ 
    Q.ncall(task.step1, task) 
    .then(function(result1){ 
     if(result1 == 'some failure state I want to cause abortion') 
     {// the rest of the task chain is unnecessary 
      console.log('aborting!'); 
      throw new Error('abort promise chain'); 
      return null; 
     } 
     return Q.ncall(task.step2, task); 
    }) 
    .then(function(result2){ 
     console.log('doing step 3...'); 
     return Q.ncall(task.step3, task); 
    }) 
    .fail(function(err) { 
     if (err.message === 'abort promise chain') { 
      // just swallow error because chain was intentionally aborted 
     } 
     else { 
      // else let the error bubble up because it's coming from somewhere else 
      throw err; 
     } 
    }) 
    .end(); 
} 
+17

コントロールフローの例外を使用していますが、これは通常お勧めしません。 Kris Kowalによって与えられた解は、その問題を回避します。 –

+3

'' throw null 'の後に 'return null'は必要ありません。 – Pepijn

34

これは分岐する必要がある場合です。これは、ネストまたはサブルーチンの作成を意味します。

function doTask(task, callback) { 
    return Q.ncall(task.step1, task) 
    .then(function(result1) { 
     if (result1) return result1; 
     return Q.ncall(task.step2, task) 
     .then(function(result2) { 
      return Q.ncall(task.step3, task); 
     }) 
    }) 
    .nodeify(callback) 
} 

それとも

function doTask(task, callback) { 
    return Q.ncall(task.step1, task) 
    .then(function(result1) { 
     if (result1) { 
      return result1; 
     } else { 
      return continueTasks(task); 
     } 
    }) 
    .nodeify(callback) 
} 

function continueTasks(task) { 
    return Q.ncall(task.step2, task) 
    .then(function(result2) { 
     return Q.ncall(task.step3, task); 
    }) 
} 
+0

これは分岐のための最良のアプローチですか?これは、複数の枝があるときにインデントを再度導入するようです。 [例](https://gist.github.com/svenjacobs/3f42bbaf4cbabe2b58b5)では、[q-io](https://github.com/kriskowal/q-io)を使って複数のファイル操作を実行します。まず、あるディレクトリが存在するかどうかをチェックし、あるファイルを探してファイルをリストし、一致するファイルが1つだけ見つかった場合はそのファイルを削除します。そこにチェーンを中止すべき複数のif条件があります。私は特別な戻り値を使用してそのケースをチェックしますが、すべての関数でチェックする必要があります。これは良いアプローチですか? –

+4

@SvenJacobsこの例で説明しているのは例外の良い例です。検討してくださいhttps://gist.github.com/kriskowal/e98774443eb0f1653871 –

+2

私はまだエラー処理のための難しいため、このアプローチの問題があります。約束チェーン(Calvin Alvinの答え)内に投げ入れることで、フロー中にエラーを処理する単一の '.fail()'を持つことができます。このような約束を書いて(分岐)コールバック地獄に私を連れて行く。 – Pedro

2

私はあなたが約束を破る約束を拒否しなければならないと信じています。

https://github.com/kriskowal/q/wiki/API-Reference#qrejectreason

もそれは(.ENDのように思える)私が見つけた

function doTask(task, callback) 
{ 
    Q.ncall(task.step1, task) 
    .then(function(result1){ 
     if(result1) 
     {// the rest of the task chain is unnecessary 
      console.log('aborting!'); 
      // by calling Q.reject, your second .then is skipped, 
      // only the .fail is executed. 
      // result1 will be passed to your callback in the .fail call 
      return Q.reject(result1); 
     } 
     return Q.ncall(task.step2, task); 
    }) 
    .then(function(result2){ 
     console.log('doing step 3...'); 
     return Q.ncall(task.step3, task); 
    }) 
    .fail(callback).done(); 
} 
関連する問題