When on some part of code execution unexpected situation happens will be created and thrown Error
object. Then going up by call stack JavaScript
interpreter will check for try/catch
logic on each next
level until the end of call stack resulting to termination of the program.
Error Object
Error
is a most basic type of exception object in JavaScript
. Also this object is often used as a custom error objects prototype.
Standard forces exception to have at least two fields:
name
- defined type of errormessage
- human readable description of exception, which should give some error context
On most modern browsers Error
object contains stack
field, which contains information about call stack of execution, which may be useful to get a context of exception.
Built-in JS
error types:
-
RangeError
- usually thrown, when out of expected input provided. e.g. following code will throwRangeError
:var pi = 3.14159 pi.toFixed(100000)
-
ReferenceError
- is thrown on attempt to access non existing variable -
SyntaxError
- is thrown, when syntactically invalid provided to theJS
interpreter -
TypeError
- thrown, when there is an attempt to use a type in a not appropriate way. e.g. trying to execute non-existing method -
URIError
- thrown byencodeURI
ordecodeURI
, when not validurl
provided -
EvalError
- thrown byeval
function
Error Handling
There is a special construct to handle errors in JavaScript
:
try {
// executing code
} catch (err) {
// handling possible error
} finally {
// this will be executed both in success and error cases
}
catch block stops propagation of error further up on the call stack and allows to react to the error happened.
When there is a need to differentiate reaction on different kinds of errors it is possible to use instanceof
operator to distinguish error type:
try {
// ...
} catch (err) {
if (err instanceof TypeError) {
// handling TypeError
} else if (err instanceof ReferenceError) {
// handling ReferenceError
} else {
// handle any other error type, e.g.
throw err
}
}
It is possible to re-throw caught error any number of times.
Async Error Handling
When dealing with asynchronous code try/catch
clause may not catch possible exceptions. This may happen because asynchronous function running outside of call stack, calling it.
try {
throwingAsyncFunc() // this function throws exception asynchronously
} catch (err) {
// exception will not be caught here
}
If throwingAsyncFunc
returns Promise
it is possible to use async/await
pattern to handle it's errors with try/catch
block:
async function handler() {
try {
await throwingAsyncFunc() // this function throws exception asynchronously
} catch (err) {
// exception will be caught here
}
}
Error Event
In JavaScript
errors are also events. So it is possible to react on this events. For example in browser api it is possible to do with following event listener:
window.addEventListener("error", function (e) {
console.log(e.error)
})
Such error handler will catch unhandled errors from any execution context.
Moreover, addEventListener
allows to attach to the error
event variety of different handlers.
This allows to create many centralized handlers for different error types.
For Node.Js
:
process.on("uncaughtException", error => {
// handle error
})
It is also useful to use error events and not directly throw an error when dealing with streams.
source.run(stream => {
stream
.on("data", data => {
// handle arriving piece of data
})
.on("end", result => {
// handling end of stream
})
.on("error", err => {
// handling possible error during the stream
})
})
Custom Errors
If there is a need to catch and re-throw some custom exception up it is a good practice to maintain a context of previous exception:
class CustomError extends Error {
constructor(message, err) {
super(message)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, CustomError)
}
this.wrappedError = err
this.name = "CustomError"
this.date = new Date()
}
}