MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
JavaScriptの基礎 - エラーハンドリングのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
JavaScriptの基礎 - エラーハンドリング
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == エラーハンドリングは、プログラムの実行中に発生する予期しない問題を検出・処理するための仕組みである。<br> JavaScriptでは、<code>try / catch / finally</code> 構文を使用して同期処理のエラーを捕捉し、<code>.catch()</code> や <code>async/await</code> を使用して非同期処理のエラーを処理する。<br> <br> 適切なエラーハンドリングを実装することにより、以下に示すメリットが得られる。<br> * プログラムのクラッシュ防止 *: エラーが発生しても処理を継続できる * デバッグの効率化 *: エラーの原因を特定しやすくなる * ユーザー体験の向上 *: エラー発生時に適切なフィードバックを提供できる * セキュリティの強化 *: 機密情報を含むスタックトレースの漏洩を防止できる <br><br> == try / catch / finally == <code>try / catch / finally</code> は、JavaScriptにおける同期処理のエラーハンドリングの基本構文である。<br> <code>catch</code> または <code>finally</code> のどちらかは省略可能だが、両方を同時に省略することはできない。<br> <br> ==== 基本構文 ==== <code>try</code> ブロック内でエラーが発生すると、処理は即座に <code>catch</code> ブロックに移行する。<br> <br> <code>finally</code> ブロックは、エラーの有無にかかわらず必ず実行される。<br> <br> <syntaxhighlight lang="javascript"> try { // エラーが発生する可能性のある処理 const result = riskyOperation(); console.log(result); } catch (error) { // エラーが発生した場合の処理 console.error('エラーが発生しました:', error.message); } finally { // 成功・失敗にかかわらず常に実行される処理 console.log('処理が完了しました'); } </syntaxhighlight> <br> ==== catchブロック ==== <code>catch</code> ブロックは、<code>try</code> ブロック内で発生したエラーオブジェクトを引数として受け取る。<br> <br> ES2019以降、エラーオブジェクトが不要な場合は、括弧を省略した <u>オプショナルcatch binding</u> を使用できる。<br> <br> <syntaxhighlight lang="javascript"> // 通常のcatch (エラーオブジェクトを受け取る) try { JSON.parse('invalid json'); } catch (error) { console.error(error.name); // SyntaxError console.error(error.message); // エラーメッセージ } // オプショナルcatch binding (ES2019以降) try { riskyOperation(); } catch { console.error('エラーが発生しました'); } </syntaxhighlight> <br> エラーの種類に応じて処理を分岐させることもできる。<br> <br> <syntaxhighlight lang="javascript"> try { processData(input); } catch (error) { if (error instanceof TypeError) { console.error('型エラー:', error.message); } else if (error instanceof RangeError) { console.error('範囲エラー:', error.message); } else { console.error('予期しないエラー:', error.message); throw error; // 対処できないエラーは再スロー } } </syntaxhighlight> <br> ==== finallyブロック ==== <code>finally</code> ブロックは、<code>try</code> や <code>catch</code> 内に <code>return</code> 文や <code>throw</code> 文があっても必ず実行される。<br> ファイルのクローズやDB接続の解放など、リソースの後処理に使用する。<br> <br> <syntaxhighlight lang="javascript"> function readFile(filename) { let fileHandle = null; try { fileHandle = openFile(filename); return fileHandle.read(); } catch (error) { console.error('ファイル読み込みエラー:', error.message); return null; } finally { // return や throw の後でも必ず実行される if (fileHandle) { fileHandle.close(); } } } </syntaxhighlight> <br> ==== ネストしたtry / catch ==== <code>try / catch</code> はネストして使用できる。<br> 内側の <code>catch</code> で処理しきれないエラーは、外側の <code>catch</code> に伝播させることができる。<br> <br> <syntaxhighlight lang="javascript"> try { try { riskyDatabaseCall(); } catch (dbError) { // 内側のcatchでエラーをラップして再スロー throw new Error('Database operation failed', { cause: dbError }); } } catch (error) { // 外側のcatchで最終的なエラー処理 logError(error); } </syntaxhighlight> <br><br> == Errorオブジェクト == JavaScriptには、エラーの種類ごとに専用の組み込みErrorクラスが用意されている。<br> <br> 全ての組み込みエラーは、<code>Error</code> クラスを継承している。<br> <br> ==== 組み込みErrorの種類 ==== 下表に、JavaScriptの主な組み込みエラーの種類を示す。<br> <br> <center> {| class="wikitable" |+ 組み込みErrorの種類 ! エラー型 !! 発生条件 !! 例 |- | <code>Error</code> || 汎用エラー || <u>throw new Error('message')</u> |- | <code>TypeError</code> || 型が無効 || <u>null.property</u>、<u>'str'.forEach()</u> |- | <code>ReferenceError</code> || 未定義変数の参照 || <u>console.log(undefinedVar)</u> |- | <code>SyntaxError</code> || 構文エラー || <u>eval("let x =;")</u> |- | <code>RangeError</code> || 値が範囲外 || <u>new Array(-1)</u> |- | <code>URIError</code> || 無効なURI || <u>decodeURIComponent('%')</u> |- | <code>EvalError</code> || eval関連 (後方互換) || ほぼ使用されない |- | <code>AggregateError</code> || 複数エラーの集約 || <u>Promise.any()</u> で全失敗時 |} </center> <br> ==== Errorのプロパティ ==== 下表に、<code>Error</code> オブジェクトの主なプロパティを示す。<br> <br> <center> {| class="wikitable" |+ Errorオブジェクトのプロパティ ! プロパティ !! 説明 |- | <code>name</code> || エラー名 (例: "TypeError") |- | <code>message</code> || エラーメッセージ |- | <code>stack</code> || スタックトレース (非標準だがほぼ全環境で利用可能) |- | <code>cause</code> || エラーの原因 (ES2022) |} </center> <br> <code>cause</code> プロパティを使用すると、エラーの根本原因を連鎖的に保持できる。<br> <br> <syntaxhighlight lang="javascript"> try { // 何らかの処理 } catch (originalError) { const wrappedError = new Error('処理に失敗しました', { cause: originalError }); console.error(wrappedError.cause); // 元のエラーにアクセス可能 throw wrappedError; } </syntaxhighlight> <br><br> == throw文 == <code>throw</code> 文は、任意の値を例外としてスローするための構文である。<br> <br> スローされた値は、呼び出しスタックを遡って最初の <code>catch</code> ブロックで捕捉される。<br> <br> ==== 基本構文 ==== <code>throw</code> 文の基本的な使い方を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> // 文字列をスロー (非推奨) throw 'エラーが発生しました'; // Errorオブジェクトをスロー (推奨) throw new Error('エラーが発生しました'); // 条件に基づいてスロー function divide(a, b) { if (b === 0) { throw new Error('ゼロ除算はできません'); } return a / b; } </syntaxhighlight> <br> 文字列や数値等、任意の値をスローすることは技術的には可能だが、<code>Error</code> オブジェクトのスローが強く推奨される。<br> その理由は以下の通りである。<br> <br> * <u>スタックトレースの自動キャプチャ</u> *: エラー発生箇所の追跡が容易になる。 * <u>標準プロパティ (name, message, stack) の提供</u> *: エラー情報へのアクセス方法が統一される。 * <u>デバッガやエラーモニタリングツールとの互換性</u> *: Sentry等のツールが正しく認識できる。 <br> ==== Errorオブジェクトのスロー ==== 組み込みエラーを適切に使い分けることで、エラーの原因を明確に伝えることができる。<br> <br> <syntaxhighlight lang="javascript"> function processUserInput(input) { if (typeof input !== 'string') { throw new TypeError('inputは文字列である必要があります'); } if (input.length === 0) { throw new RangeError('inputは1文字以上である必要があります'); } if (!isKnownVariable(input)) { throw new ReferenceError(`"${input}" は定義されていません`); } return input.trim(); } try { processUserInput(42); } catch (error) { console.error(`${error.name}: ${error.message}`); // TypeError: inputは文字列である必要があります } </syntaxhighlight> <br><br> == カスタムエラークラス == 組み込みエラーに加え、アプリケーション固有のエラーを表現するためにカスタムエラークラスを作成することができる。<br> <br> カスタムエラークラスは、<code>Error</code> クラスを継承して定義する。<br> <br> ==== 基本的なカスタムエラー ==== 最もシンプルなカスタムエラークラスの定義方法を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> class ValidationError extends Error { constructor(message) { super(message); this.name = 'ValidationError'; } } // 使用例 function validateAge(age) { if (age < 0 || age > 150) { throw new ValidationError(`年齢の値が不正です: ${age}`); } return age; } try { validateAge(-5); } catch (error) { if (error instanceof ValidationError) { console.error('バリデーションエラー:', error.message); } } </syntaxhighlight> <br> ==== 実用的なカスタムエラー ==== 追加プロパティを持つカスタムエラーや、エラー階層の構築例を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> // HTTPエラーの基底クラス class HTTPError extends Error { constructor(statusCode, message) { super(message); this.name = 'HTTPError'; this.statusCode = statusCode; } } // 404 Not Found専用エラー class NotFoundError extends HTTPError { constructor(resource) { super(404, `${resource} not found`); this.name = 'NotFoundError'; } } // 403 Forbidden専用エラー class ForbiddenError extends HTTPError { constructor(resource) { super(403, `Access to ${resource} is forbidden`); this.name = 'ForbiddenError'; } } // 使用例 function fetchResource(path, user) { if (!resourceExists(path)) { throw new NotFoundError(path); } if (!user.hasPermission(path)) { throw new ForbiddenError(path); } return getResource(path); } try { fetchResource('/admin/data', currentUser); } catch (error) { if (error instanceof NotFoundError) { res.status(404).send(error.message); } else if (error instanceof ForbiddenError) { res.status(403).send(error.message); } else if (error instanceof HTTPError) { res.status(error.statusCode).send(error.message); } else { res.status(500).send('内部サーバーエラー'); } } </syntaxhighlight> <br> ES2022の <code>Error.prototype.cause</code> を利用すると、エラーの根本原因を連鎖的に保持できる。<br> <br> <syntaxhighlight lang="javascript"> async function connectDatabase(config) { try { await db.connect(config); } catch (originalError) { throw new Error('データベース接続に失敗しました', { cause: originalError }); } } try { await connectDatabase(config); } catch (error) { console.error(error.message); // データベース接続に失敗しました console.error(error.cause.message); // 元のエラーメッセージ } </syntaxhighlight> <br><br> == 非同期処理のエラーハンドリング == 非同期処理では、同期処理とは異なるエラーハンドリングの方法が必要になる。<br> <br> Promiseベースの処理には <code>.catch()</code> メソッドを、<code>async / await</code> には <code>try / catch</code> を使用する。<br> <br> ==== Promiseのエラー捕捉 ==== Promiseのエラーは、<code>.catch()</code> メソッドで捕捉する。<br> <br> <code>.then()</code> の第2引数でも捕捉できるが、成功ハンドラ内のエラーを捕捉できないため非推奨である。<br> <br> <syntaxhighlight lang="javascript"> // .catch()を使用した方法 (推奨) fetch('/api/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('エラー:', error.message)); // .then()の第2引数を使用した方法 (非推奨) // 成功ハンドラ (第1引数) 内のエラーを捕捉できない fetch('/api/data') .then( data => console.log(data), error => console.error('エラー:', error.message) ); </syntaxhighlight> <br> ==== async / awaitのエラー捕捉 ==== <code>async/await</code> を使用した非同期処理では、<code>try / catch</code> でエラーを捕捉できる。<br> <br> <syntaxhighlight lang="javascript"> async function fetchUserData(userId) { try { const response = await fetch(`/api/users/${userId}`); if (!response.ok) { throw new HTTPError(response.status, `HTTPエラー: ${response.status}`); } return await response.json(); } catch (error) { console.error('データの取得に失敗しました:', error.message); throw error; // 必要に応じて呼び出し元に再スロー } } // 呼び出し側 async function main() { try { const user = await fetchUserData(123); console.log(user); } catch (error) { console.error('ユーザデータを取得できませんでした'); } } </syntaxhighlight> <br> ==== 複数の非同期処理のエラー捕捉 ==== 複数の非同期処理を並列実行する場合、<code>Promise.all</code>、<code>Promise.allSettled</code>、<code>Promise.any</code>、<code>Promise.race</code> の違いに注意が必要である。<br> <br> 下表に各メソッドの特性を示す。<br> <br> <center> {| class="wikitable" |+ 複数の非同期処理メソッドの比較 ! メソッド !! 成功条件 !! 失敗条件 !! 返却値 !! 用途 |- | <code>Promise.all</code> || 全て成功 || 1つでも失敗 || 結果の配列 || 全て成功が必要な場合 |- | <code>Promise.allSettled</code> || 常に完了 || なし || {status, value/reason} の配列 || 全結果を個別確認する場合 |- | <code>Promise.any</code> || 1つでも成功 || 全て失敗 || 最初の成功値 || フォールバックが必要な場合 |- | <code>Promise.race</code> || 最速の完了 || 最速の完了 || 最初の完了値 || タイムアウト処理 |} </center> <br> 各メソッドの使用例を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> const p1 = fetch('/api/users'); const p2 = fetch('/api/posts'); const p3 = fetch('/api/comments'); // Promise.all: 1つでも失敗すると全体が失敗 try { const [users, posts, comments] = await Promise.all([p1, p2, p3]); } catch (error) { console.error('いずれかのリクエストが失敗:', error.message); } // Promise.allSettled: 全ての結果を個別に確認できる const results = await Promise.allSettled([p1, p2, p3]); results.forEach((result, index) => { if (result.status === 'fulfilled') { console.log(`リクエスト${index + 1}成功:`, result.value); } else { console.error(`リクエスト${index + 1}失敗:`, result.reason); } }); // Promise.any: 最初に成功したものを使用 (全て失敗するとAggregateError) try { const data = await Promise.any([ fetch('/api/primary'), fetch('/api/fallback1'), fetch('/api/fallback2') ]); } catch (error) { // AggregateError: error.errors に個別のエラーが格納される console.error('全てのリクエストが失敗:', error.errors); } </syntaxhighlight> <br><br> == エラーハンドリングのパターン == ==== ガード節パターン ==== ガード節パターンは、早期リターンを使用して条件のネストを浅くするパターンである。<br> <br> バリデーションや前提条件のチェックに有効で、ソースコードの可読性が向上する。<br> <br> ガード節を使用しない場合と使用する場合の比較を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> // ガード節なし (深いネスト) function processUser(user) { if (user) { if (user.isActive) { if (user.email) { sendEmail(user.email); return true; } else { console.error('メールアドレスがありません'); return false; } } else { console.error('ユーザーがアクティブではありません'); return false; } } else { console.error('ユーザーが存在しません'); return false; } } // ガード節あり (フラットな構造) function processUser(user) { if (!user) { console.error('ユーザーが存在しません'); return false; } if (!user.isActive) { console.error('ユーザーがアクティブではありません'); return false; } if (!user.email) { console.error('メールアドレスがありません'); return false; } sendEmail(user.email); return true; } </syntaxhighlight> <br> ==== Resultパターン ==== Resultパターンは、<code>throw</code> を使用せず、成功・失敗を表すオブジェクトを返すパターンである。<br> <br> <code>{ success, data, error }</code> のような形式で結果を返すことにより、呼び出し側が明示的にエラーを処理するよう強制できる。<br> <br> <syntaxhighlight lang="javascript"> // Resultパターンの実装 async function fetchUser(userId) { try { const response = await fetch(`/api/users/${userId}`); if (!response.ok) { return { success: false, data: null, error: `HTTPエラー: ${response.status}` }; } const data = await response.json(); return { success: true, data, error: null }; } catch (error) { return { success: false, data: null, error: error.message }; } } // 呼び出し側 async function main() { const result = await fetchUser(123); if (!result.success) { console.error('取得失敗:', result.error); return; } console.log('取得成功:', result.data); } </syntaxhighlight> <br> 下表に、Resultパターンの主なメリットを示す。<br> <br> <center> {| class="wikitable" |+ Resultパターンのメリット ! メリット !! 説明 |- | エラーの明示的な処理 || 呼び出し側がエラーチェックを忘れにくい。 |- | 例外の伝播を防止 || 意図しない場所でのエラー処理を避けられる。 |- | TypeScriptとの相性が良い || 型定義によりエラー処理を型安全に行える。 |} </center> <br><br> == 関連情報 == * [[JavaScriptの基礎 - コールバック関数]] * [[JavaScriptの基礎 - クラス]] * [[JavaScriptの基礎 - 継承とプロトタイプ]] * [[JavaScriptの基礎 - デバッグ]] <br><br> {{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux |image=/resources/assets/MochiuLogo_Single_Blue.png }} __FORCETOC__ [[カテゴリ:Web]]
JavaScriptの基礎 - エラーハンドリング
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse