web-dev-qa-db-ja.com

async / awaitの場合にエラー処理を適切に実装する方法

カスタムライブラリなしでasync/awaitecma6標準を使用しています。

現時点では、エラーを適切にキャッチしてスローする方法がわかりません。複数のasync/await関数があり、重大なエラーが発生した場合は、エラーをすべての非同期関数の先頭にスローして、関数の実行を停止します。

Async/await関数から例外をスローし、それをターゲット関数でキャッチしようとしましたが、node.jsでエラーが発生します。

    this.basicAuthLogin= async function(user)
{
    "use strict";
    const login = new Login(this.Host, this.url, user, user.pw);

    //getSessionID throws error
    this.sessionID = getSessionID(result.request.response);
}

(ノード:13964)UnhandledPromiseRejectionWarning:未処理のPromise拒否(拒否ID:1):エラー:getSessionID応答が未定義です(ノード:13964)[DEP0018] DeprecationWarning:未処理のPromise拒否は非推奨です。将来、処理されないPromise拒否は、ゼロ以外の終了コードでNode.jsプロセスを終了します。デバッガーが付属しています。

したがって、非同期関数から例外をスローしたり、node.jsのpromiseのcatchブロックで例外を再スローしたりすることは許可されていないようです。

では、どうすればこれを機能させることができますか? async関数でエラーをキャッチし、promiseでエラーを返し、非同期関数から再スローすることになっていますか?

   this.basicAuthLogin= async function(user)
{
    "use strict";
    const login = new Login(this.Host, this.url, user, user.pw);
   try{
    //getSessionID throws error
    this.sessionID = getSessionID(result.request.response);
   } catch(err) { return err;}
}

しかし、これは、最初の非同期関数からの呼び出しスタックでは、すべての関数が非同期である必要があり、実際には必要ない場合でも、約束を待たなければならないことを意味します。

誰かが私を啓発してくれることを願っています。

よろしくルヴィ

基本的なコールスタックの擬似コードを編集します。

   async startTest[arr]{

    for (var i = 0; i < arr.length; i++)
    {
      try {
          await runStep(arr[i];
        } catch(err) { 
            console.log(err);
            break; 
        }
      }
    }

  async runStep(step)
  {
     try {
     var userIsValid = await validateUser(step.user);
     var req = buildRequest(step.request);
     var result = await sendRequest(req);
     var verify = verifyResult();
     } catch(err){ throw err;}
  }

  async validateUser(user)
  {
     //make checks
     //
     var result = await this.authenticate(parameter).catch(err => {throw err});
     userFound = true;
   }

  function authenticate(parameter) {
  //can throw async function
   basicAuthLogin(parameter).catch(err => {throw err};

   }

  function async basicAuthLogin(parameter()
  {
   try {
    //can throw  async function
      var result = await request(parameter);
      //can throw sync function
      this.sessionID = getSessionID(response);
      //can throw   sync function
      } catch(err) { throw err; }
   }
7
Ruvi

async/awaitの優れた点の1つは、try/catchが非同期コードを処理できることです。

最初のbasicAuthLogin関数はまったく問題ありません(getSessionID同期関数である場合)。 そうでない場合は、awaitがありません。[あなたは今それがそうだと言った])。コードusingbasicAuthLoginは、スローする可能性を処理する必要があります(エラーを処理するか、呼び出し元に伝播できるようにすることで、それを処理する責任があります)。だからどちらか:

// In an `async` function
try {
    await this.basicAuthLogin(/*...*/);
} catch (e) {
    // Handle the fact something failed
}

または

// NOT in an `async` function:
this.basicAuthLogin(/*...*/)
    .catch(e => { /* Handle the fact something failed */ });

それを使用するコードがこれらの2つのことのいずれかを実行する場合(またはエラーをこれらの2つのことのいずれかを実行するコードに伝播させる場合)、「未処理の拒否」エラーは発生しません。

getSessionIDが非同期かどうかを尋ねる私のコメントに応えて、あなたは次のように書いています。

いいえ、非同期ではありません。コールスタックの5階または6階をキャッチしたいという例外をスローする単純な関数ですが、それは許可されていないようです。

これが実際の例ですそれを行うこと(私の場合、basicAuthLogingetSessionIDの前に非同期のものを実際に使用するようにしましたが、実際には問題ではありません) :

const getSessionID = () => {
  throw new Error("Failed");
};
const somethingAsync = () => new Promise(resolve => {
  setTimeout(resolve, 100);
});
const basicAuthLogin = async function(user)
{
    "use strict";
    await somethingAsync();
    /*
    const login = new Login(this.Host, this.url, user, user.pw);
    */

    //getSessionID throws error
    getSessionID();
};

const wrapper1 = async () => {
  await basicAuthLogin();
};
const wrapper2 = async () => {
  await wrapper1();
};
const wrapper3 = async () => {
  await wrapper2();
};

// Top-level caller
(async () => {
  try {
    await wrapper3();
  } catch (e) {
    console.log("Caught error: " + e.message);
  }
})();

ルールは例外を除いて同じです(概念的にはこれらの例外であるため):

  1. それを処理するか(例:try/catch)、または呼び出し元に伝播させ(通常は何もしないことで)、

  2. トップレベルはそれを処理しなければなりません

そのルール#2は、非asyncコードからasyncコード(通常はスタックの一番上)に移行するときにラッパーが必要であることを意味します。どちらか:

(async () => {
  try {
    await theFirstAsyncFunction();
    await theNextAsyncFunction();
    await aThirdAsyncFunction();
  } catch (e) {
    // handle the error
  }
})();

または

(async () => {
  await theFirstAsyncFunction();
  await theNextAsyncFunction();
  await aThirdAsyncFunction();
})().catch(e => { /* handle the error */});

またはもちろん:

theFirstAsyncFunction()
.then(() => theNextAsyncFunction())
.then(() => aThirdAsyncFunction())
.catch(e => { /* handle the error */});

共通の分母は次のとおりです。トップレベルは常にエラーを処理します。

17
T.J. Crowder