※実務でエラーハンドリングがちゃんとできていなかった結果テストにも影響が出た。
自分自身try/catchなどの理解が足りてないと感じたので学習の記録として残しておきます。
try/catch
tryブロックの中で実行される関数の中でthrow Errorされた時、throwされたErrorをcatchブロックで受け取れる。受け取ったerrorをどう扱うのかを決めて例外発生時の対応を行う。
Promiseがrejectの状態の時、axiosの場合は自動でrejectしてくれるが、fetchの場合は自動でthrow Errorをしてくれないため自分でthrow Errorを記述する必要がある。
同期関数での例
// 失敗条件で throw する同期関数
function parseJsonStrict(input: string) {
if (!input) throw new Error('入力が空です');
// JSON.parse 自体も不正な文字列なら内部で throw します
return JSON.parse(input);
}
try {
const data = parseJsonStrict('not-json'); // ← ここで throw
console.log('成功:', data);
} catch (e) {
console.error('捕捉したエラー:', (e as Error).message);
} finally {
console.log('後片付け(必ず実行)');
}
非同期関数での例
// fetch は 4xx/5xx でも reject しないため、自分で throw して「失敗」にする
async function fetchUser(userId: number) {
const res = await fetch(`/api/users/${userId}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`); // ← ここで throw すると下の catch に入る
const body = await res.json();
if (!body.isActive) throw new Error('このユーザーは非アクティブです'); // 業務エラーの例
return body as { id: number; name: string; isActive: boolean };
}
(async () => {
try {
const user = await fetchUser(1); // ← await しているので、reject は catch で受け取れる
console.log('成功:', user);
} catch (e) {
console.error('捕捉したエラー:', (e as Error).message);
}
})();
Promise チェインでの例
function getNumberAsync(): Promise<number> {
return new Promise((resolve) => setTimeout(() => resolve(10), 10));
}
getNumberAsync()
.then((n) => {
if (n < 100) throw new Error('100未満はエラーにしたい'); // ← ここで throw
return n * 2;
})
.then((m) => {
console.log('成功:', m);
})
.catch((e) => {
console.error('捕捉したエラー:', (e as Error).message); // 上の throw がここに来る
});
Promise
オブジェクトです。3つの状態があります。
Pending: 初期状態、非同期処理が実行中の状態。
Fulfilled: 非同期処理が問題なく完了した状態。Promiseは結果を保持してる
Rejected: 非同期処理が失敗した状態。Promiseが失敗した理由を保持してる
非同期処理
同期処理と違って時間がかかるもの。時間がかかる処理を同期で行うと、他の処理がストップしてしまうので非同期処理を使うことによって同期処理を止めることなく実行できるようにする仕組み。
asyncをつけると非同期処理として認識される。
非同期処理のブロックの中でawaitを活用すると、awaitがついた関数がresolvedするまで次の処理が行われなくなる。
//非同期処理の実行順序一例
console.log("A");
(async () => {
console.log("B");
await Promise.resolve(); // ここで関数を中断(マイクロタスクに延期)
console.log("C"); // ← Promise が解決した “あと” に実行
})();
console.log("D");
//A → B → D → Cの順番
//非同期処理のブロックの中ではPromiseが解決されるまで次に進まない
//---------------------------------------------------------------
export const asyncFn = async () => 12;
console.log('A');
asyncFn().then(v => console.log('then:', v));
console.log('B');
// 実出力: A → B → then: 12
//---------------------------------------------------------------
console.log('A');
setTimeout(() => console.log('T'), 0);
(async () => {
console.log('B');
await Promise.resolve(); // マイクロタスク
console.log('C');
})();
console.log('D');
// => A, B, D, C, T (マイクロタスク C はタイマー T より先)
//マイクロタスク、マクロタスクの関係
例外(throw)
例外にはErrorオブジェクトを使用する。使用場面は、API fetch処理が成功したけど、特定の場合はエラーにしたい時など。try/catchと組み合わせた時に、catchブロックでthrowしたerrorを受け取れる。return new Error(...) は失敗にならない(ただの値)。必ず throw を使う。