プロミスチェーンで複数のキャッチを処理する 質問する

プロミスチェーンで複数のキャッチを処理する 質問する

私はまだ Promise に慣れておらず、現在は bluebird を使用していますが、最適な対処方法がよくわからないシナリオがあります。

たとえば、Express アプリ内に次のような Promise チェーンがあります。

repository.Query(getAccountByIdQuery)
        .catch(function(error){
            res.status(404).send({ error: "No account found with this Id" });
        })
        .then(convertDocumentToModel)
        .then(verifyOldPassword)
        .catch(function(error) {
            res.status(406).send({ OldPassword: error });
        })
        .then(changePassword)
        .then(function(){
            res.status(200).send();
        })
        .catch(function(error){
            console.log(error);
            res.status(500).send({ error: "Unable to change password" });
        });

私が求めている動作は次のようになります。

  • IDでアカウントを取得する
  • この時点で拒否された場合は、エラーを返します。
  • エラーがなければ、返されたドキュメントをモデルに変換します
  • データベースドキュメントでパスワードを確認する
  • パスワードが一致しない場合は、エラーが発生し、別のエラーが返されます。
  • エラーがない場合はパスワードを変更してください
  • その後成功を返します
  • 何か問題があった場合は500を返してください

したがって、現在のところ、キャッチは連鎖を停止しないようで、それは理にかなっています。そのため、エラーに基づいて連鎖を特定の時点で強制的に停止させる方法があるかどうか、または、 の場合のように、何らかの分岐動作を取得するためにこれを構造化するより良い方法があるかどうか疑問に思っていますif X do Y else Z

どんな助けでも大歓迎です。

ベストアンサー1

この動作は同期スローとまったく同じです。

try{
    throw new Error();
} catch(e){
    // handle
} 
// this code will run, since you recovered from the error!

.catchこれは、エラーから回復できるようにするという目的の半分です。状態がまだエラーであることを通知するために、再スローすることが望ましい場合があります。

try{
    throw new Error();
} catch(e){
    // handle
    throw e; // or a wrapper over e so we know it wasn't handled
} 
// this code will not run

ただし、エラーは後のハンドラーによってキャッチされるため、これだけではうまくいきません。ここでの本当の問題は、一般化された「何でも処理する」エラー ハンドラーは一般的に悪い習慣であり、他のプログラミング言語やエコシステムでは非常に嫌われているということです。このため、Bluebird は型付きキャッチと述語キャッチを提供しています。

追加の利点は、ビジネス ロジックが要求/応答サイクルをまったく意識する必要がない (意識する必要がない) ことです。クライアントが取得する HTTP ステータスとエラーを決定するのはクエリの役割ではありません。後でアプリが成長するにつれて、ビジネス ロジック (DB をクエリする方法とデータを処理する方法) とクライアントに送信する内容 (HTTP ステータス コード、テキスト、応答) を分離する必要がある場合があります。

コードの書き方は次のとおりです。

まず、.QueryをスローしNoSuchAccountError、Bluebird が既に提供している からサブクラス化しますPromise.OperationalError。エラーをサブクラス化する方法がよくわからない場合は、お知らせください。

さらにサブクラス化してAuthenticationError、次のようなことを行います。

function changePassword(queryDataEtc){ 
    return repository.Query(getAccountByIdQuery)
                     .then(convertDocumentToModel)
                     .then(verifyOldPassword)
                     .then(changePassword);
}

ご覧のとおり、非常にわかりやすく、プロセスで何が起こるかのマニュアルのようにテキストを読むことができます。また、リクエスト/レスポンスから分離されています。

ここで、ルート ハンドラーから次のように呼び出します。

 changePassword(params)
 .catch(NoSuchAccountError, function(e){
     res.status(404).send({ error: "No account found with this Id" });
 }).catch(AuthenticationError, function(e){
     res.status(406).send({ OldPassword: error });
 }).error(function(e){ // catches any remaining operational errors
     res.status(500).send({ error: "Unable to change password" });
 }).catch(function(e){
     res.status(500).send({ error: "Unknown internal server error" });
 });

この方法では、ロジックがすべて 1 か所に集まり、クライアントへのエラーの処理方法の決定もすべて 1 か所に集まり、互いに混乱することがなくなります。

おすすめ記事