JavaScriptでのasync・awaitの例外処理方法

プログラミング

JavaScriptのasync・awaitについては以前の記事で簡単に触れましたが、例外処理を行う場合には別途注意が必要であるため、続きの記事を書きます。

async・awaitで例外処理を行う場合、メソッドチェーンで受け取る方法と、try-catchで受け取る方法があります。
これらの方法について、紹介していこうと思います。

サンプルコードはNode.jsで実行しています。


awaitで呼び出される関数では、Promiseオブジェクトを返す必要があります。
通常はresolveで返しますが、例外処理を行う場合はrejectを返す必要があります。
これは、例外をメソッドチェーンで受け取る場合も、try-catchで受け取る場合も変わりません。

なお、rejectで返されたPromiseオブジェクトをメソッドチェーンでもtry-catchでも受け取らなかった場合は、UnhandledPromiseRejectionWarningが発生しプロセスが終了するので、注意が必要です。


rejectでPromiseオブジェクトを返された場合は、メソッドチェーン「.catch」で受け取ることが可能です。
「.catch」で受け取った場合は、「.catch」内の処理が実行され、戻り値は取得できません。
(戻り値はundefinedになります)
resolveで返された場合は、「.catch」内の処理が実行されません。

なお、メソッドチェーン「.catch」の前にメソッドチェーン「.then」を入れることで、resolveで返されたPromiseオブジェクトをこれで返すことができます。
「.then」で受け取った場合は、「.then」内の処理が実行され、戻り値は取得できません。
(戻り値はundefinedになります)
また、rejectで返された場合は、「.catch」の前に定義した「.then」内の処理が実行されません。

以下、サンプルコードです。

【サンプルコード】

・sample.js

function lightTask() {
  console.log("light");
}

function heavyTask() {
  // awaitで呼び出される関数ではpromiseオブジェクトを返す必要がある
  return new Promise((resolve, reject) => {
    const procedure = () => {
      console.log("heavy");
      // resolve(0);  // ①
      // reject(1);  // ②
    }
    setTimeout(procedure, 1000);
  });
}

// awaitを使用する関数にはasyncをつける
async function exection() {
  let result = await heavyTask()
  //               .then((code) => { console.log("normal:" + code); }) // ③
  //               .catch((code) => { console.error("error:" + code); }) // ④
                 ;
  console.log("code:"+result);
  lightTask();
}

exection();

【実行結果】

・①と③と④のコメントアウトを外した場合(resolveを.thenで受け取る)

C:\tmp>node sample.js
heavy
normal:0
code:undefined
light

C:\tmp>

・①と④のコメントアウトを外した場合(resolveを.thenで受け取らない)

C:\tmp>node sample.js
heavy
code:0
light

C:\tmp>

②と③と④のコメントアウトを外した場合(rejectを.catchで受け取る)

C:\tmp>node sample.js
heavy
error:1
code:undefined
light

C:\tmp>

参考:②と③のコメントアウトを外した場合(rejectを受け取らない)

C:\tmp>node sample.js
heavy
(node:9180) UnhandledPromiseRejectionWarning: 1
(node:9180) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This
error originated either by throwing inside of an async function without a catch
block, or by rejecting a promise which was not handled with .catch(). To termina
te the node process on unhandled promise rejection, use the CLI flag `--unhandle
d-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejectio
ns_mode). (rejection id: 1)
(node:9180) [DEP0018] DeprecationWarning: Unhandled promise rejections are depre
cated. In the future, promise rejections that are not handled will terminate the
 Node.js process with a non-zero exit code.

C:\tmp>

rejectで返されたPromiseオブジェクトはtry-catchで受け取ることも可能です。
ただし、この場合、戻り値を参照しないように注意が必要で、参照してしまうとUnhandledPromiseRejectionWarningが発生しプロセスが終了してしまいます。

以下、サンプルコードです。

【サンプルコード】

・sample.js

function lightTask() {
  console.log("light");
}

function heavyTask() {
  // awaitで呼び出される関数ではpromiseオブジェクトを返す必要がある
  return new Promise((resolve, reject) => {
    const procedure = () => {
      console.log("heavy");
      reject(1);
    }
    setTimeout(procedure, 1000);
  });
}

// awaitを使用する関数にはasyncをつける
async function exection() {
  try {
    let result = await heavyTask();
    console.log("code:"+result);
  } catch (e) {
    console.error("error:"+e);
    // console.log("code:"+result); // ⑤
  }
  lightTask();
}

exection();

【実行結果】

・そのまま実行した場合(rejectをtry-catchで受け取る)

C:\tmp>node sample.js
heavy
error:1
light

C:\tmp>

・参考:⑤のコメントアウトを外した場合(try-catchで受け取る際に戻り値参照)

C:\tmp>node sample.js
heavy
error:1
(node:1600) UnhandledPromiseRejectionWarning: ReferenceError: result is not defi
ned
    at exection (C:\tmp\sample.js:23:25)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:1600) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This
error originated either by throwing inside of an async function without a catch
block, or by rejecting a promise which was not handled with .catch(). To termina
te the node process on unhandled promise rejection, use the CLI flag `--unhandle
d-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejectio
ns_mode). (rejection id: 1)
(node:1600) [DEP0018] DeprecationWarning: Unhandled promise rejections are depre
cated. In the future, promise rejections that are not handled will terminate the
 Node.js process with a non-zero exit code.

C:\tmp>

いかがでしたでしょうか。

JavaScriptのasync・await、特に例外処理は文法に少し癖があり、誤った記述をしても異常終了しないことがあるので、実際のシステム開発でも障害になりやすい所です。
awaitで待っていないように見える、例外発生時にだけ予期せぬ事象が出る、といった場合は、async・awaitの書き方が正しいか疑ってみましょう。

コメント

タイトルとURLをコピーしました