なぜ「try-catch ラッパー」が業務で効いてくるのか
まず前提として、JavaScript の try-catch は「例外が投げられても、アプリ全体を落とさずに処理を続けるための仕組み」です。
try {
riskyOperation();
} catch (err) {
console.error("エラーが発生しました", err);
}
JavaScriptただ、これをあちこちにベタ書きしていくと、コードがこうなります。
try { ... } catch (e) { ... }
try { ... } catch (e) { ... }
try { ... } catch (e) { ... }
JavaScript同じパターンが何度も出てきて、
「毎回同じようなエラーログ」「毎回同じようなフォールバック処理」を書くことになります。
ここで登場するのが「try-catch ラッパー」です。
ざっくり言うと、
「危ない処理を渡すと、“成功か失敗か”を分かりやすい形で返してくれる小さなユーティリティ」
です。
これを一つ用意しておくと、業務コードのあちこちで「安全に実行する」というパターンを、きれいに共通化できます。
基本形:同期処理用の try-catch ラッパー
成功・失敗をオブジェクトで返すパターン
まずは、同期処理(普通の関数)を対象にした、いちばん基本的なラッパーです。
function tryCatch(fn) {
try {
const value = fn();
return { ok: true, value };
} catch (error) {
return { ok: false, error };
}
}
JavaScript使い方はとてもシンプルです。
const result = tryCatch(() => {
// ここに「落ちるかもしれない処理」を書く
JSON.parse('{"id":1}');
});
if (result.ok) {
console.log("成功:", result.value);
} else {
console.error("失敗:", result.error);
}
JavaScriptここで重要なのは、「例外を投げる関数」を「例外を投げない関数」に変換している、という発想です。
元の世界
「呼ぶと例外が飛ぶかもしれない関数」
ラッパー後の世界
「呼ぶと { ok: true/false, value/error } を返すだけの関数」
これによって、呼び出し側は try-catch を書かずに、「結果を見て分岐する」だけで済むようになります。
デフォルト値を返す try-catch ラッパー
「失敗したらこの値でいい」という場面
「エラーの詳細はいらないから、失敗したらデフォルト値でいい」という場面も多いです。
その場合は、次のようなラッパーが便利です。
function tryCatchOr(fn, defaultValue) {
try {
return fn();
} catch {
return defaultValue;
}
}
JavaScript使い方の例です。
const value1 = tryCatchOr(
() => JSON.parse('{"id":1}'),
{}
);
const value2 = tryCatchOr(
() => JSON.parse("壊れたJSON"),
{}
);
console.log(value1); // { id: 1 }
console.log(value2); // {}(失敗したのでデフォルト)
JavaScriptここでのポイントは、「呼び出し側が try/catch を書かなくてよくなる」ことと、
「失敗時の振る舞い(デフォルト値)を、呼び出し側が明示できる」ことです。
非同期処理(Promise / async)用の try-catch ラッパー
async/await 版の try-catch ラッパー
業務では、非同期処理(API 呼び出し、DB アクセスなど)が多いので、async 関数や Promise に対応したラッパーもほぼ必須です。
async function tryCatchAsync(fn) {
try {
const value = await fn();
return { ok: true, value };
} catch (error) {
return { ok: false, error };
}
}
JavaScript使い方の例です。
async function fetchUser() {
const res = await fetch("/api/user");
if (!res.ok) throw new Error("HTTP エラー");
return res.json();
}
(async () => {
const result = await tryCatchAsync(fetchUser);
if (result.ok) {
console.log("ユーザー:", result.value);
} else {
console.error("取得失敗:", result.error);
}
})();
JavaScriptここでも、「例外を投げるかもしれない async 関数」を、
「常に { ok, value, error } を返す関数」に変換しています。
非同期版の tryCatchOr
「失敗したらこの値でいい」という非同期版もよく使います。
async function tryCatchAsyncOr(fn, defaultValue) {
try {
return await fn();
} catch {
return defaultValue;
}
}
JavaScript使い方です。
const user = await tryCatchAsyncOr(
fetchUser,
{ id: null, name: "ゲスト" }
);
JavaScriptこれで、「API が落ちても、最低限のデフォルトユーザーで画面を描画する」といった振る舞いを簡単に書けます。
「ラッパーを返すラッパー」にするパターン
関数を包んで「安全版関数」を作る
毎回 tryCatch(() => fn()) と書くのが面倒な場合、
「関数を受け取って、安全版の関数を返す」ユーティリティにしてしまう手もあります。
function wrapTryCatch(fn) {
return function wrapped(...args) {
try {
const value = fn(...args);
return { ok: true, value };
} catch (error) {
return { ok: false, error };
}
};
}
JavaScript使い方です。
function parseJson(text) {
return JSON.parse(text);
}
const safeParseJson = wrapTryCatch(parseJson);
const r1 = safeParseJson('{"id":1}');
const r2 = safeParseJson("壊れたJSON");
console.log(r1.ok, r1.value); // true, { id: 1 }
console.log(r2.ok, r2.error); // false, Error(...)
JavaScriptこの形にしておくと、「危ない関数を全部“安全版”に差し替える」という設計がしやすくなります。
非同期版も同じ発想で書けます。
function wrapTryCatchAsync(fn) {
return async function wrapped(...args) {
try {
const value = await fn(...args);
return { ok: true, value };
} catch (error) {
return { ok: false, error };
}
};
}
JavaScriptどこまで「安全」にするかを決める視点
try-catch ラッパーを設計するとき、次のような軸で考えると整理しやすいです。
エラー情報を返したいか(スタックトレースなど)
失敗時にデフォルト値を返したいか
同期・非同期どちらを対象にするか
関数をその場で実行するか、「安全版関数」を返すか
例えば、
「バッチ処理で、1 レコードごとに成功・失敗を集計したい」
→ { ok, value, error } 形式が向いている
「画面表示用に、“ダメならデフォルト値でいい”という軽い処理」
→ tryCatchOr / tryCatchAsyncOr が向いている
といった感じで使い分けられます。
実務での具体的な利用イメージ
ログ出力や JSON 処理と組み合わせる
以前やった「安全な JSON parse / stringify」と try-catch ラッパーは相性抜群です。
例えば、safeJsonParse を自前で書かずに、try-catch ラッパーで表現することもできます。
function safeJsonParse(text) {
return tryCatch(() => JSON.parse(text));
}
JavaScriptあるいは、「ログ出力時に絶対落ちたくない」というときに、safeJsonStringify と組み合わせてこう書けます。
function logDebug(label, payload) {
const jsonResult = tryCatch(() => JSON.stringify(payload));
if (jsonResult.ok) {
console.debug(label, jsonResult.value);
} else {
console.debug(label, "[unserializable]", jsonResult.error.message);
}
}
JavaScriptUI イベントハンドラを安全にする
ボタンクリックなどのイベントハンドラで例外が飛ぶと、
フレームワークによっては画面が壊れたり、以降の処理が動かなくなったりします。
そこで、「イベントハンドラを安全版で包む」というパターンもよく使います。
function wrapHandler(handler) {
return function safeHandler(event) {
const result = tryCatch(() => handler(event));
if (!result.ok) {
console.error("イベント処理中にエラー:", result.error);
}
};
}
// 使用例(仮想的な UI フレームワーク)
button.onclick = wrapHandler(() => {
riskyOperation();
});
JavaScript小さな練習で感覚をつかむ
次のような「たまに落ちる関数」を自分で用意して、tryCatch, tryCatchOr, tryCatchAsync, wrapTryCatch などを試してみてください。
function sometimesFail() {
if (Math.random() < 0.5) {
throw new Error("たまたま失敗");
}
return "成功";
}
JavaScriptこの関数をそのまま呼んだ場合と、try-catch ラッパー経由で呼んだ場合で、
「呼び出し側のコードがどれくらいスッキリするか」「例外にどう向き合えるか」を比べてみると、
「try-catch ラッパーをユーティリティとして持つ意味」が体感として分かってきます。
