技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

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>