
上記では、人々が日常生活で犯す過ちについて話しました。 プログラミングのエラーは別のものです。 エラーメッセージは、コードの改善に役立ちます。プロジェクトのユーザーに何か問題が発生したことを通知し、エラーが発生しないように動作をユーザーに伝えることができます。
このJavaScriptエラー処理資料は、3つの部分に分かれています。 最初に、JavaScriptのエラー処理システムの概要を説明し、エラーオブジェクトについて説明します。 その後、サーバーコードで発生するエラー(特にNode.js + Express.jsバンドルを使用する場合)で何をすべきかという質問に対する答えを探します。 次に、React.jsでのエラー処理について説明します。 ここで検討されるフレームワークは、非常に人気があるため選択されます。 ただし、ここで説明するエラーの処理の原則は普遍的であるため、ExpressとReactを使用しなくても、学んだことを作業ツールに簡単に適用できます。
この資料で使用されているデモプロジェクトのコードは、 このリポジトリにあります。
1. JavaScriptのエラーとそれらを扱う普遍的な方法
コードに問題が発生した場合は、次の構成を使用できます。
throw new Error('something went wrong')
このコマンドの実行中に、このオブジェクトでErrorオブジェクトのインスタンスが作成され、例外が生成されます(または、「スロー」と呼ばれます)。 throwステートメントは、任意の式を含む例外をスローできます。 この場合、エラーを処理するための対策が講じられていない場合、スクリプトの実行は停止します。
通常、初心者のJSプログラマーは
throw
ステートメントを使用しません。 通常、言語ランタイムまたはサードパーティライブラリによってスローされた例外が発生します。 これが発生すると、
ReferenceError: fs is not defined
ようなものがコンソールに入り
ReferenceError: fs is not defined
、プログラムの実行は停止します。
▍オブジェクトエラー
Error
オブジェクトのインスタンスには、使用できるいくつかのプロパティがあります。 私たちが興味を持つ最初のプロパティは
message
です。 これは、引数としてエラーコンストラクターに渡すことができる行を取得する場所です。 たとえば、次の例は、
Error
オブジェクトのインスタンスの作成と、
message
プロパティにアクセスしてデザイナーが渡した文字列のコンソールへの出力を示しています。
const myError = new Error('please improve your code') console.log(myError.message) // please improve your code
非常に重要なオブジェクトの2番目のプロパティは、エラースタックのトレースです。 これは
stack
プロパティです。 これに目を向けると、コールスタック(エラー履歴)を表示できます。これは、プログラムの誤動作を引き起こした一連の操作を示しています。 特に、これにより、どのファイルに不良コードが含まれているかを理解し、エラーにつながった関数呼び出しのシーケンスを確認できます。
stack
プロパティにアクセスすることで表示できるものの例を次に示します。
Error: please improve your code at Object.<anonymous> (/Users/gisderdube/Documents/_projects/hacking.nosync/error-handling/src/general.js:1:79) at Module._compile (internal/modules/cjs/loader.js:689:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) at Module.load (internal/modules/cjs/loader.js:599:32) at tryModuleLoad (internal/modules/cjs/loader.js:538:12) at Function.Module._load (internal/modules/cjs/loader.js:530:3) at Function.Module.runMain (internal/modules/cjs/loader.js:742:12) at startup (internal/bootstrap/node.js:266:19) at bootstrapNodeJSCore (internal/bootstrap/node.js:596:3)
ここで、上部にはエラーメッセージがあり、その後に実行がエラーの原因となったコードのセクションが示され、この失敗したセクションが呼び出された場所が説明されています。 これは、エラーコードフラグメントに関して「最も遠い」まで続きます。
▍生成とエラー処理
Error
オブジェクトのインスタンスを作成する、つまり、
new Error()
という形式のコマンドを実行しても、特別な結果は生じません。 エラーを生成する
throw
ステートメントを適用すると、興味深いことが起こり始めます。 既に述べたように、このようなエラーが処理されない場合、スクリプトの実行は停止します。 同時に、
throw
演算子がプログラマーによって使用されたかどうか、特定のライブラリーまたは言語ランタイム(ブラウザーまたはNode.js)でエラーが発生したかどうかに違いはありません。 さまざまなエラー処理シナリオについて話しましょう。
▍建設しよう...キャッチ
try...catch
は、しばしば忘れられるエラーを処理する最も簡単な方法です。 ただし、今日では、
async/await
構造のエラーを処理するために使用できるため、以前よりもはるかに集中的に使用され
async/await
ます。
このブロックは、同期コードで発生するエラーを処理するために使用できます。 例を考えてみましょう。
const a = 5 try { console.log(b) // b - } catch (err) { console.error(err) // } console.log(a) // ,
この例で、失敗した
console.log(b)
コマンドを
try...catch
で囲まなかった場合、スクリプトは停止します。
▍最後にブロック
エラーが発生したかどうかに関係なく、一部のコードを実行する必要がある場合があります。 これを行うには、
try...catch
コンストラクトで3番目のオプションの
finally
ブロックを使用できます。 多くの場合、その使用は
try...catch
直後に来るコードと同等
try...catch
が、状況によっては便利になる場合があります。 以下にその使用例を示します。
const a = 5 try { console.log(b) // b - } catch (err) { console.error(err) // } finally { console.log(a) // }
非同期メカニズム-コールバック
JavaScriptでプログラミングするときは、非同期で実行されるコードのセクションに常に注意を払う必要があります。 非同期関数があり、その中にエラーが発生した場合、スクリプトは引き続き実行されます。 JSの非同期メカニズムがコールバックを使用して実装される場合(これはお勧めしません)、対応するコールバック(コールバック関数)は通常2つのパラメーターを受け取ります。 これは、エラーおよび
result
含む可能性のある
err
パラメーターのようなもので、非同期操作の結果を伴います。 次のようになります。
myAsyncFunc(someInput, (err, result) => { if(err) return console.error(err) // console.log(result) })
コールバックでエラーが発生した場合、エラーパラメータとして表示されます。 それ以外の場合、このパラメーターは値
undefined
または
null
を取得し
null
。
err
何か
err
あることが判明した場合、この例では
return
コマンドを使用する
if...else
、
if...else
を使用して
else
ブロックにコマンドを配置し、非同期操作の結果を処理するため、これに反応することが重要です。 エラーが発生した場合、結果、つまりこの場合は値
undefined
持つ可能性のある
result
パラメーターを処理する可能性を排除することです。 この値を操作すると、たとえば、オブジェクトが含まれていると想定される場合、それ自体がエラーを引き起こす可能性があります。 これは、
result.data
コンストラクトなどを使用しようとしたときに発生します。
synchronous非同期メカニズム-約束
JavaScriptで非同期操作を実行するには、コールバックではなくプロミスを使用することをお勧めします。 ここでは、コードの可読性の向上に加えて、より高度なエラー処理メカニズムがあります。 つまり、promiseを使用する場合、コールバック関数に分類される可能性のあるエラーオブジェクトを気にする必要はありません。 この目的のために、特別な
catch
ブロックが提供されています。 前のpromiseで発生したすべてのエラー、または前の
catch
後のコードで発生したすべてのエラーをインターセプトし
catch
。 処理する
catch
ブロックを持たないプロミスでエラーが発生した場合、スクリプトの実行は停止しませんが、エラーメッセージは特に読みにくいことに注意してください。
(node:7741) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: something went wrong (node:7741) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. */
その結果、Promiseを使用する場合は、いつでも
catch
を使用することをお勧めでき
catch
。 例を見てみましょう。
Promise.resolve(1) .then(res => { console.log(res) // 1 throw new Error('something went wrong') return Promise.resolve(2) }) .then(res => { console.log(res) // }) .catch(err => { console.error(err) // , , return Promise.resolve(3) }) .then(res => { console.log(res) // 3 }) .catch(err => { // , - console.error(err) })
synchronous非同期メカニズムとtry ... catch
JavaScriptに
async/await
コンストラクトが表示された後、エラーを処理する従来の方法に戻りました。 このアプローチでエラーを処理するのは非常に簡単で便利です。 例を考えてみましょう。
;(async function() { try { await someFuncThatThrowsAnError() } catch (err) { console.error(err) // } console.log('Easy!') // })()
このアプローチでは、非同期コードのエラーは同期の場合と同じ方法で処理されます。 その結果、必要に応じて、1つの
catch
、より広範囲のエラーを処理できるようになりました。
2.サーバーコードでのエラーの生成と処理
エラーを処理するためのツールが用意できたので、実際の状況でそれらを使用して何ができるかを見てみましょう。 エラーを生成して正しく処理することは、サーバー側プログラミングの重要な側面です。 エラーを処理するには、さまざまなアプローチがあります。 ここでは、フロントエンドまたはサーバーAPIを使用するメカニズムに便利に渡される
Error
オブジェクトとエラーコードのインスタンスに独自のコンストラクターを使用するアプローチを示します。 特定のプロジェクトのバックエンドがどのように構成されているかは、実際には問題ではありません。どのアプローチでも、エラーの処理に関して同じアイデアを使用できるからです。
ルーティングを担当するサーバーフレームワークとして、Express.jsを使用します。 効果的なエラー処理システムを編成するために必要な構造について考えてみましょう。 必要なものは次のとおりです。
- 普遍的なエラー処理は、エラーを処理するのに適した基本的なメカニズムです。その間、
Something went wrong, please try again or contact us
なかったというメッセージSomething went wrong, please try again or contact us
このシステムは特にインテリジェントではありませんが、少なくとも何かがうまくいかなかったことをユーザーに知らせることができます。 このようなメッセージは、「無限ダウンロード」またはそのようなものよりもはるかに優れています。 - 特定のエラーの処理-不適切なシステム動作の原因に関する詳細情報をユーザーに通知し、問題の対処方法に関する具体的なアドバイスをユーザーに提供できるメカニズム。 たとえば、これは、ユーザーがサーバーに送信するリクエストにいくつかの重要なデータがないこと、またはデータベースに特定のレコードが既に追加されているなどに関連する場合があります。
errorエラーオブジェクトの独自のコンストラクターの開発
ここでは、標準の
Error
クラスを使用して拡張します。 JavaScriptで継承メカニズムを使用するのは危険ですが、この場合、これらのメカニズムは非常に便利です。 なぜ継承が必要なのですか? 実際のところ、コードを便利にデバッグするには、エラースタックのトレースに関する情報が必要です。 標準の
Error
クラスを拡張すると、余分な労力なしでスタックをトレースできます。 独自のエラーオブジェクトに2つのプロパティを追加します。 1つ目は
code
プロパティで、これは
err.code
形式の構造を使用してアクセスできます。 2番目は
status
プロパティです。 これは、アプリケーションのクライアント部分に送信される予定のHTTPステータスコードを記録します。
これは、コードがモジュールとして設計されている
CustomError
クラスの外観です。
class CustomError extends Error { constructor(code = 'GENERIC', status = 500, ...params) { super(...params) if (Error.captureStackTrace) { Error.captureStackTrace(this, CustomError) } this.code = code this.status = status } } module.exports = CustomError
▍ルーティング
エラーオブジェクトを使用する準備ができたので、ルート構造を構成する必要があります。 前述のように、エラー処理に対する統一されたアプローチを実装する必要があります。これにより、すべてのルートのエラーを等しく処理できます。 デフォルトでは、Express.jsフレームワークはそのようなスキームを完全にはサポートしていません。 実際には、すべてのルートがカプセル化されています。
この問題に対処するために、独自のルートハンドラを実装し、通常の関数の形式でルートロジックを定義できます。 このアプローチのおかげで、ルート関数(または他の関数)がエラーをスローした場合、それはルートハンドラーに分類され、アプリケーションのクライアント部分に渡すことができます。 サーバーでエラーが発生した場合、JSON-APIがこれに使用されると想定して、次の形式でフロントエンドにエラーを転送する予定です。
{ error: 'SOME_ERROR_CODE', description: 'Something bad happened. Please try again or contact support.' }
この段階で何が起こっているのか理解できない場合は、心配しないでください。読み続け、議論されていることを試してみてください。徐々に理解していきます。 実際、コンピュータートレーニングに関しては、一般的なアイデアが最初に議論され、次に詳細への移行が実行されるときに、ここでは「トップダウン」アプローチが使用されます。
これがルートハンドラコードの外観です。
const express = require('express') const router = express.Router() const CustomError = require('../CustomError') router.use(async (req, res) => { try { const route = require(`.${req.path}`)[req.method] try { const result = route(req) // route res.send(result) // , route } catch (err) { /* , route */ if (err instanceof CustomError) { /* - */ return res.status(err.status).send({ error: err.code, description: err.message, }) } else { console.error(err) // // - return res.status(500).send({ error: 'GENERIC', description: 'Something went wrong. Please try again or contact support.', }) } } } catch (err) { /* , , , , , , */ res.status(404).send({ error: 'NOT_FOUND', description: 'The resource you tried to access does not exist.', }) } }) module.exports = router
コード内のコメントがそれを非常によく説明していると信じています。 それらのコードを読むことは、その後のコードの説明よりも便利であることを願っています。
ルートファイルを見てみましょう。
const CustomError = require('../CustomError') const GET = req => { // return { name: 'Rio de Janeiro' } } const POST = req => { // throw new Error('Some unexpected error, may also be thrown by a library or the runtime.') } const DELETE = req => { // , throw new CustomError('CITY_NOT_FOUND', 404, 'The city you are trying to delete could not be found.') } const PATCH = req => { // CustomError try { // - throw new Error('Some internal error') } catch (err) { console.error(err) // , throw new CustomError( 'CITY_NOT_EDITABLE', 400, 'The city you are trying to edit is not editable.' ) } } module.exports = { GET, POST, DELETE, PATCH, }
これらの例では、クエリ自体は何も実行されません。 エラーの発生について、さまざまなシナリオを単純に考慮します。 したがって、たとえば、
GET /city
リクエストは
const GET = req =>...
関数
const GET = req =>...
に分類され、
POST /city
リクエストは
const POST = req =>...
関数
const POST = req =>...
分類されます。 このスキームは、クエリパラメータを使用する場合にも機能します。 たとえば、フォーム
GET /city?startsWith=R
一般的に、エラーを処理する際、フロントエンドは、再試行またはサーバー所有者への連絡の申し出のみを含む一般エラー、または問題に関する詳細情報を含む
CustomError
コンストラクターを使用して生成されるエラーのいずれかを取得することが実証されています。
一般的なエラーデータは、次の形式でアプリケーションのクライアント部分に送られます。
{ error: 'GENERIC', description: 'Something went wrong. Please try again or contact support.' }
CustomError
コンストラクターは次のように使用されます。
throw new CustomError('MY_CODE', 400, 'Error description')
これにより、次のJSONコードがフロントエンドに渡されます。
{ error: 'MY_CODE', description: 'Error description' }
アプリケーションのサーバー部分で徹底的に作業したので、役に立たないエラーログはクライアント部分に分類されなくなりました。 代わりに、クライアントは何がうまくいかなかったかに関する有用な情報を受け取ります。
ここにあるコードを含むリポジトリがここにあることを忘れないでください。 ダウンロードして試してみて、必要に応じてプロジェクトのニーズに合わせて調整してください。
3.クライアントでエラーを処理する
次に、フロントエンドに関するエラー処理システムの3番目の部分について説明します。 ここでは、まずアプリケーションのクライアント部分で発生したエラーを処理する必要があり、次にサーバーで発生したエラーについてユーザーに通知する必要があります。 まず、サーバーエラー情報を表示します。 既に述べたように、この例ではReactライブラリーが使用されます。
errorエラー状態をアプリケーション状態で保存する
他のデータと同様に、エラーとエラーメッセージは変更される可能性があるため、コンポーネントの状態にすることは理にかなっています。 コンポーネントがマウントされると、エラーデータがリセットされるため、ユーザーが最初にページを表示したときにエラーメッセージは表示されません。
次に対処するのは、同じタイプのエラーを同じスタイルで表示する必要があるということです。 サーバーと同様に、ここでは3種類のエラーを区別できます。
- グローバルエラー-このカテゴリには、サーバーから発生する一般的な性質のエラーメッセージ、または他の同様の状況でユーザーがシステムにログインしていない場合に発生するエラーなどが含まれます。
- アプリケーションのサーバー側で生成される特定のエラー-これには、サーバーから報告されるエラーが含まれます。 たとえば、ユーザーがログインしてユーザー名とパスワードをサーバーに送信しようとしたときに、パスワードが間違っていることをサーバーが通知した場合、同様のエラーが発生します。 そのようなことは、アプリケーションのクライアント部分ではチェックされないため、そのようなエラーに関するメッセージはサーバーから送信されます。
- アプリケーションのクライアント部分によって生成された特定のエラー。 このようなエラーの例は、対応するフィールドに入力された無効なメールアドレスに関するメッセージです。
2番目と3番目のタイプのエラーは非常によく似ており、1レベルのコンポーネントの状態ストアを使用して操作できます。 彼らの主な違いは、それらが異なるソースから来ていることです。 以下では、コードを分析して、それらの使用を検討します。
Reactでアプリケーションの状態を管理するために組み込みシステムを使用しますが、必要に応じて、MobXやReduxなどの状態を管理するための特別なソリューションを使用することもできます。
▍グローバルエラー
通常、このようなエラーメッセージは、ステータスとともに最上位のコンポーネントに保存されます。 これらは、静的ユーザーインターフェイス要素に表示されます。 これは、画面上部の赤いボックス、モーダルウィンドウ、またはその他のものです。 実装は特定のプロジェクトに依存します。 これがエラーメッセージの表示です。

グローバルエラーメッセージ
ここで、
Application.js
ファイルに保存されているコードを見てください。
import React, { Component } from 'react' import GlobalError from './GlobalError' class Application extends Component { constructor(props) { super(props) this.state = { error: '', } this._resetError = this._resetError.bind(this) this._setError = this._setError.bind(this) } render() { return ( <div className="container"> <GlobalError error={this.state.error} resetError={this._resetError} /> <h1>Handling Errors</h1> </div> ) } _resetError() { this.setState({ error: '' }) } _setError(newError) { this.setState({ error: newError }) } } export default Application
ご覧のように、この状態では
Application.js
にエラーデータを保存する場所があります。 , .
GlobalError
,
x
, .
GlobalError
(
GlobalError.js
).
import React, { Component } from 'react' class GlobalError extends Component { render() { if (!this.props.error) return null return ( <div style={{ position: 'fixed', top: 0, left: '50%', transform: 'translateX(-50%)', padding: 10, backgroundColor: '#ffcccc', boxShadow: '0 3px 25px -10px rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', }} > {this.props.error} <i className="material-icons" style={{ cursor: 'pointer' }} onClick={this.props.resetError} > close </font></i> </div> ) } } export default GlobalError
if (!this.props.error) return null
. , . . , , , . , ,
x
, - , .
, ,
_setError
Application.js
. , , , , (
error: 'GENERIC'
). (
GenericErrorReq.js
).
import React, { Component } from 'react' import axios from 'axios' class GenericErrorReq extends Component { constructor(props) { super(props) this._callBackend = this._callBackend.bind(this) } render() { return ( <div> <button onClick={this._callBackend}>Click me to call the backend</button> </div> ) } _callBackend() { axios .post('/api/city') .then(result => { // - , }) .catch(err => { if (err.response.data.error === 'GENERIC') { this.props.setError(err.response.data.description) } }) } } export default GenericErrorReq
, . , , . . -, , -, UX-, , — .
▍ ,
, , , .

, . . .
SpecificErrorReq.js
.
import React, { Component } from 'react' import axios from 'axios' import InlineError from './InlineError' class SpecificErrorRequest extends Component { constructor(props) { super(props) this.state = { error: '', } this._callBackend = this._callBackend.bind(this) } render() { return ( <div> <button onClick={this._callBackend}>Delete your city</button> <InlineError error={this.state.error} /> </div> ) } _callBackend() { this.setState({ error: '', }) axios .delete('/api/city') .then(result => { // - , }) .catch(err => { if (err.response.data.error === 'GENERIC') { this.props.setError(err.response.data.description) } else { this.setState({ error: err.response.data.description, }) } }) } } export default SpecificErrorRequest
, , ,
x
. , , . , , — , , , . , , . , , , — .
▍,
, , , . , , - . .

,
SpecificErrorFrontend.js
, .
import React, { Component } from 'react' import axios from 'axios' import InlineError from './InlineError' class SpecificErrorRequest extends Component { constructor(props) { super(props) this.state = { error: '', city: '', } this._callBackend = this._callBackend.bind(this) this._changeCity = this._changeCity.bind(this) } render() { return ( <div> <input type="text" value={this.state.city} style={{ marginRight: 15 }} onChange={this._changeCity} /> <button onClick={this._callBackend}>Delete your city</button> <InlineError error={this.state.error} /> </div> ) } _changeCity(e) { this.setState({ error: '', city: e.target.value, }) } _validate() { if (!this.state.city.length) throw new Error('Please provide a city name.') } _callBackend() { this.setState({ error: '', }) try { this._validate() } catch (err) { return this.setState({ error: err.message }) } axios .delete('/api/city') .then(result => { // - , }) .catch(err => { if (err.response.data.error === 'GENERIC') { this.props.setError(err.response.data.description) } else { this.setState({ error: err.response.data.description, }) } }) } } export default SpecificErrorRequest
▍
, , (
GENERIC
), , . , , , , , , , , . .
まとめ
Webアプリケーションでエラーを処理する方法を理解していただけたことを願っています。このようなもの
console.error(err)
は、デバッグ目的でのみ使用する必要があります。プログラマーが忘れたものは、本番環境に侵入しないでください。loglevelのような適切なライブラリを使用して、ロギングの問題の解決を簡素化します。
親愛なる読者! プロジェクトのエラーをどのように処理しますか?
