1. ホーム
  2. javascript

[解決済み] プロミスチェインで複数のキャッチを処理する

2022-05-14 15:32:24

質問

私はまだプロミスの初心者で、現在ブルーバードを使用していますが、あるシナリオがあり、それにどのように対処するのがベストかよくわかりません。

例えば、私はexpressアプリの中で次のようなプロミスチェーンを持っています。

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を返す

現在、catch は連鎖を止めないようで、それは理にかなっています。そこで、エラーに基づいてある時点で連鎖を強制的に止める方法があるかどうか、あるいは、ある形式の分岐動作を得るためにこれを構造化する良い方法があるかどうか、例えば if X do Y else Z .

何か手助けがあれば幸いです。

どのように解決するのですか?

この動作はまさにsynchronous throwのようなものです。

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

のポイントの半分はそこです。 .catch - の目的の半分は、エラーから回復することができるようにすることです。状態がまだエラーであることを知らせるためにrethrowすることが望ましいかもしれません。

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

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

追加の利点は、ビジネスロジックがリクエスト/レスポンスサイクルを全く意識する必要がない(べきではない)ことです。クライアントがどのHTTPステータスとエラーを取得するかを決めるのはクエリの責任ではありませんし、アプリが成長するにつれて、ビジネスロジック(DBへの問い合わせ方法とデータの処理方法)とクライアントに送るもの(どのHTTPステータスコード、どのテキスト、どの応答)を分離したいと思うかもしれません。

以下は、私がコードを書く方法です。

まず、私は .Query を投げるようにします。 NoSuchAccountError からサブクラス化します。 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" });
 });

こうすることで、ロジックはすべて一箇所に集まり、クライアントへのエラー処理の判断も一箇所に集まり、互いに混乱することがありません。