JavaScriptでは、非同期処理を扱う際にPromiseとtry-catchを使ってエラー処理が行われます。
APIからデータを取得する場合やダイアログ操作など、一般的な開発シーンでも頻繁に使用されます。
本記事では具体例を交えながら、エラーハンドリングについて解説しているので、処理の順番や出力結果を予想しながらご覧いただければ幸いです。
Promiseについて
Promiseは以下の3つの状態を持ちます。
- 待機(Pending):初期状態または処理中の状態。
- 成功(Fulfilled):処理が成功して完了した状態。
- 拒否(Rejected):処理が失敗した状態。
以下の処理では、count.valueが偶数の場合は成功状態、それ以外の場合は拒否状態となります。
const getPromise = () => {
return new Promise((resolve, reject) => {
if (count.value % 2 === 0) {
resolve("test: promise resolve");
} else {
reject("test: promise reject");
}
});
};
以下の処理では、Promiseチェーンを使って結果を処理しています。
getPromise()
.then((message) => {
console.log(message); // "test: promise resolve"が出力される
})
.catch((message) => {
console.log(message); // "test: promise reject"が出力される
});
-
resolve関数が呼ばれると、Promiseは成功(Fulfilled)状態になり、thenメソッドに渡されたコールバック関数が実行されます。
この場合、"test: promise resolve"がコンソールログに出力されます。 -
reject関数が呼ばれると、Promiseは拒否(Rejected)状態になり、catchメソッドに渡されたコールバック関数が実行されます。
この場合、"test: promise reject"がコンソールログに出力されます。
try-catchについて
tryブロック内で発生したエラーをcatchブロックで捕まえることができます。
以下の処理では"try-error"がコンソールログに出力されます。
try {
// 明示的にエラーを投げています
throw "try-error";
} catch (e) {
console.log(e); // "try-error"が出力される
}
Promiseチェーンとtry-catchを用いたエラーハンドリング
2つのパターンを用いて解説します。
パターン①
以下の処理でgetPromiseがresolveまたはrejectを呼び出した場合のコンソールログはどうなるでしょうか。
※awaitがついている関数については、処理が完了するまで待機状態になります。
async function syncProcess1() {
try {
await getPromise()
.then(() => {
console.log("promise-resolve");
})
.catch(() => {
console.log("promise-reject");
});
console.log("passed-promise");
} catch (e) {
console.log("try-catch");
} finally {
console.log("try-finally");
}
}
結果
resolveの場合
- promise-resolve
- passed-promise
- try-finally
rejectの場合
- promise-reject
- passed-promise
- try-finally
解説
まず、getPromise関数はawaitが付いているため待機状態となり、resolveまたはrejectが呼ばれて処理完了するまで後続処理は実行されません。
getPromiseでresolveが呼ばれると、.thenの"promise-resolve"が出力され、rejectで拒否状態になると.catchの"promise-reject"が出力されます。
getPromiseがrejectで拒否されても、getPromiseの.catchで処理を拾うため、tryのcatchには捕まらずに後続の処理を実行します。
パターン②
次に、パターン①のコードのgetPromise関数のawaitを除いた場合の処理を考えてみましょう。
以下の処理で、getPromiseがresolveまたはrejectを呼んだ場合のコンソールログはどうなるでしょうか。
function syncProcess2() {
try {
getPromise()
.then(() => {
console.log("promise-resolve");
})
.catch(() => {
console.log("promise-reject");
});
console.log("passed-promise");
} catch (e) {
console.log("try-catch");
} finally {
console.log("try-finally");
}
}
結果
resolveの場合
- passed-promise
- try-finally
- promise-resolve
rejectの場合
- passed-promise
- try-finally
- promise-reject
解説
awaitが付いていないgetPromiseは、非同期処理となるため、後続の同期処理が優先して実行されます。
たとえ後続の同期処理が時間のかかる処理であっても、Promiseのログは一番最後に出力されます。
(JavaScriptのイベントループでは、コールスタックで同期処理、タスクキューで非同期処理を管理しており、コールスタックの処理がすべてなくなった後、タスクキューの処理が呼ばれる仕組みのため)

