throw new Error('something went wrong')
throw
statement. They, as a rule, are faced with exceptions issued by either the language runtime environment or third-party libraries. When this happens, something like ReferenceError: fs is not defined
gets to the console ReferenceError: fs is not defined
and the program execution stops.Error
object instance has several properties that we can use. The first property of interest is message
. This is exactly the line that can be passed to the error constructor as an argument. For example, the following shows the creation of an instance of the Error
object and the output to the console of a string passed by the constructor through a call to its message
property. const myError = new Error('please improve your code') console.log(myError.message) // please improve your code
stack
property. Referring to it, you can view the call stack (error history), which shows the sequence of operations that led to the incorrect operation of the program. In particular, it allows you to understand which file contains the failed code and see which sequence of function calls resulted in an error. Here is an example of what can be seen by accessing the stack
property. 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
object, that is, executing a command like new Error()
, does not lead to any special consequences. Interesting things start to happen after applying the throw
operator, which generates an error. As already mentioned, if this error is not processed, the execution of the script will stop. In this case, there is no difference whether the throw
statement was used by the programmer himself, whether an error occurred in some library or in the language runtime (in the browser or in Node.js). Let's talk about various error handling scenarios.try...catch
is the easiest way to handle errors, which is often forgotten. Nowadays, however, it is used much more intensively than before, due to the fact that it can be used to handle errors in async/await
constructions. const a = 5 try { console.log(b) // b - } catch (err) { console.error(err) // } console.log(a) // ,
console.log(b)
command in a try...catch
, the script would be stopped.try...catch
construct, use the third, optional, block finally
. Often, its use is equivalent to some kind of code that comes right after try...catch
, but in some situations it can be useful. Here is an example of its use. const a = 5 try { console.log(b) // b - } catch (err) { console.error(err) // } finally { console.log(a) // }
err
parameter, which may contain an error, and result
with the results of an asynchronous operation. It looks like this: myAsyncFunc(someInput, (err, result) => { if(err) return console.error(err) // console.log(result) })
err
parameter. Otherwise, this parameter will get the value undefined
or null
. If it turns out that there is something in err
, it is important to respond to this, either as in our example, using the return
command, or using the if...else
and placing commands in the else
block to work with the result of the asynchronous operation. The point is that, in the event that an error occurred, to exclude the possibility of working with the result, the parameter result
, which in this case can be set to undefined
. Working with this value, if it is assumed, for example, that it contains an object, can itself cause an error. Let's say it happens when you try to use a result.data
construct or the like.catch
block is provided for this purpose. It intercepts all errors that occurred in promises that are before it, or all errors that occurred in the code after the previous catch
. Note that if there was an error in the promise that does not have a catch
block for processing, this will not stop the execution of the script, but the error message will not be particularly readable. (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. */
catch
. Take a look at an example. 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) })
async/await
construction appeared in JavaScript, we returned to the classic error handling method - to try...catch...finally
. Handling errors with this approach is very easy and convenient. Consider an example. ;(async function() { try { await someFuncThatThrowsAnError() } catch (err) { console.error(err) // } console.log('Easy!') // })()
catch
you can handle a wider range of errors.Error
object and error codes that are conveniently passed to the frontend or to any mechanisms using server APIs. How the backend of a specific project is structured doesn’t matter much, as with any approach you can use the same ideas for dealing with errors.Something went wrong, please try again or contact us
, prompting the user to try the failed operation again or contact the owner of the server. This system is not particularly intelligent, but at least it is able to inform the user that something has gone wrong. This message is much better than “endless loading” or something similar.Error
class and expand it. Using inheritance mechanisms in JavaScript is risky, but in this case, these mechanisms are very useful. Why do we need inheritance? The fact is that in order for us to make the code convenient to debug, we need information about the error stack trace. Expanding the standard Error
class, we, without additional efforts, get the opportunity to trace the stack. We add two properties to our own error object. The first is the code
property, which can be accessed using an err.code
. The second is the status
property. The HTTP status code will be written to it, which is planned to be transferred to the client part of the application.CustomError
class looks, the code of which is designed as a module. 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
{ 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
request gets into the function const GET = req =>...
, the POST /city
request gets into the function const POST = req =>...
and so on. This scheme also works when using query parameters. For example - for a request of the form GET /city?startsWith=R
In general, it is demonstrated here that when handling errors, the frontend can be either a general error, containing only a suggestion to try again or contact the server owner, or an error generated using the CustomError
constructor, which contains detailed information about the problem. { error: 'GENERIC', description: 'Something went wrong. Please try again or contact support.' }
CustomError
constructor is used like this: throw new CustomError('MY_CODE', 400, 'Error description')
{ error: 'MY_CODE', description: 'Error description' }
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
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
), , . , , , , , , , , . .console.error(err)
, , , . - loglevel .Source: https://habr.com/ru/post/431078/
All Articles