This commit is contained in:
Ilya Kantor 2016-11-28 21:35:42 +03:00
parent 83b93e5992
commit 9ad9063d00
742 changed files with 884 additions and 779 deletions

View file

@ -0,0 +1,47 @@
The difference becomes obvious when we look at the code inside a function.
The behavior is different if there's a "jump out" of `try..catch`.
For instance, when there's a `return` inside `try..catch`. The `finally` clause works in case of *any* exit from `try..catch`, even via the `return` statement: right after `try..catch` is done, but before the calling code gets the control.
```js run
function f() {
try {
alert('start');
*!*
return "result";
*/!*
} catch (e) {
/// ...
} finally {
alert('cleanup!');
}
}
f(); // cleanup!
```
...Or when there's a `throw`, like here:
```js run
function f() {
try {
alert('start');
throw new Error("an error");
} catch (e) {
// ...
if("can't handle the error") {
*!*
throw e;
*/!*
}
} finally {
alert('cleanup!')
}
}
f(); // cleanup!
```
It's `finally` that guarantees the cleanup here. If we just put the code at the end of `f`, it wouldn't run.

View file

@ -0,0 +1,38 @@
importance: 5
---
# Finally or just the code?
Compare the two code fragments.
1. The first one uses `finally` to execute the code after `try..catch`:
```js
try {
work work
} catch (e) {
handle errors
} finally {
*!*
cleanup the working space
*/!*
}
```
2. The second fragment puts the cleaning right after `try..catch`:
```js
try {
work work
} catch (e) {
handle errors
}
*!*
cleanup the working space
*/!*
```
We definitely need the cleanup after the work has started, doesn't matter if there was an error or not.
Is there an advantage here in using `finally` or both code fragments are equal? If there is such an advantage, then give an example when it matters.

View file

@ -0,0 +1,609 @@
# Error handling, "try..catch"
No matter how great we are at programming, sometimes our scripts have errors. They may occur because of our mistakes, an unexpected user input, an erroneous server response and for a thousand of other reasons.
Usually, a script "dies" (immediately stops) in case of an error, printing it to console.
But there's a syntax construct `try..catch` that allows to "catch" errors and, instead of dying, do something more reasonable.
[cut]
## The "try..catch" syntax
The `try..catch` construct has two main blocks: `try`, and then `catch`:
```js
try {
// code...
} catch (err) {
// error handling
}
```
It works like this:
1. First, the code in `try {...}` is executed.
2. If there were no errors, then `catch(err)` is ignored: the execution reaches the end of `try` and then jumps over `catch`.
3. If an error occurs, then `try` execution is stopped, and the control flows to the beginning of `catch(err)`. The `err` variable (can use any name for it) contains an error object with details about what's happened.
**So, an error inside `try` does not kill the script: we have a chance to handle it in `catch`.**
Let's see the examples.
- An errorless example: shows `alert` `(1)` and `(2)`:
```js run
try {
alert('Start of try runs'); // *!*(1) <--*/!*
// ...no errors here
alert('End of try runs'); // *!*(2) <--*/!*
} catch(e) {
alert('Catch is ignored, because there are no errors'); // (3)
}
alert("...Then the execution continues");
```
- An example with error: shows `(1)` and `(3)`:
```js run
try {
alert('Start of try runs'); // *!*(1) <--*/!*
*!*
lalala; // error, variable is not defined!
*/!*
alert('End of try (never reached)'); // (2)
} catch(e) {
alert(`Error: ${e.name}`); // *!*(3) <--*/!*
}
alert("...Then the execution continues");
```
Please note that if the code structure is violated, like a figure bracket is left unclosed, then `try..catch` can't help. Such errors are fatal, the engine just cannot run the code.
There is a better term for errors that we are catching: "an exceptional situation" or just "an exception". It's much more precise, meaning exactly the situation when a already-running and well-formed code meets a problem.
For all built-in errors, the error object inside `catch` block has two main properties:
`name`
: Error name. For an undefined variable that's `"ReferenceError"`.
`message`
: Textual message about error details.
There are other non-standard properties in most environments. One of most widely used and supported everywhere is:
`stack`
: Current call stack: a string with information about the sequence of nested calls that led to the error. Used for debugging purposes.
````warn header="`try..catch` only works in synchronous code"
If an exception happens in the future code, like those inside `setTimeout`, then `try..catch` won't catch it:
```js run
try {
setTimeout(function() {
noSuchVariable; // script will die here
}, 1000);
} catch (e) {
alert( "won't work" );
}
```
That's because at the moment of running the function from `setTimeout`, the current script will have already been finished, the engine will have left `try..catch` contruct.
To catch an exception inside a scheduled function, `try..catch` must be inside that function.
````
````warn header="`try..catch` only works for runtime errors"
For `try..catch` to work, the code must be runnable. In other words, it should be valid Javascript.
It won't work if the code is syntactically wrong, for instance it has unmatched figure brackets:
```js run
try {
{{{{{{{{{{{{
} catch(e) {
alert("The engine can't understand this code, it's invalid");
}
```
The Javascript engine first reads the code, and then runs it. The errors that occur on the reading phrase are called "parse-time" errors and are unrecoverable (from inside that code). That's because the engine can't understand the code as a whole, and `try..catch` is a part of it.
So, `try..catch` can only handle errors that occur in the valid code. Such errors are called "runtime" errors.
````
## Using "try..catch"
Let's explore a real-life use case of `try..catch`.
As we already know, JavaScript supports method [JSON.parse(str)](mdn:js/JSON/parse) to read JSON-encoded values.
Usually it's used to decode the data received over the network, from the server or another source.
We receive them and call `JSON.parse`, like this:
```js run
let json = '{"name":"John", "age": 30}'; // data from the server
let user = JSON.parse(json); // reading the object
// now user is an object with properties from the string
alert( user.name ); // John
alert( user.age ); // 30
```
More detailed information about JSON you can find in the chapter <info:json>.
**If `json` is malformed, `JSON.parse` generates an error, so the script "dies".**
Are we satisfied with that? Of course, not!
This way if something's wrong with the data, the visitor will never know that (unless he opens developer console).
And people really really don't like when something "just dies" without any error message.
Let's use `try..catch` to handle the error:
```js run
let json = "{ bad json }";
try {
*!*
let user = JSON.parse(json); // <-- error happens
*/!*
alert( user.name ); // doesn't work
} catch (e) {
*!*
// ...the execution jumps here
alert( "Our apologies, the data has errors, we'll try to request them one more time." );
alert( e.name );
alert( e.message );
*/!*
}
```
Here we use `alert` only to output the message, but we can do much more: do a network request, suggest an alternative way to the visitor, send the information about the error to logging facility... All much better than just dying.
## Throwing own errors
Imagine for a minute that `json` is syntactically correct... But doesn't have a required `"name"` property:
```js run
let json = '{ "age": 30 }'; // incomplete data
try {
let user = JSON.parse(json); // <-- no errors
*!*
alert( user.name ); // no name!
*/!*
} catch (e) {
alert( "doesn't execute" );
}
```
Here `JSON.parse` runs normally, but the absense of `"name"` is actually an error for us.
To unify error handling, we'll use `throw` operator.
### "Throw" operator
The `throw` operator generates an error.
The syntax is:
```js
throw <error object>
```
Technically, we can use anything as an error object. That may be even a primitive, like a number or a string, but it's better to use objects, preferrably with `name` and `message` properties.
Javascript has many built-in constructors for standard errors: `Error`, `SyntaxError`, `ReferenceError`, `TypeError` and others. We can use them to create objects as well.
Their syntax is:
```js
let error = new Error(message);
```
For built-in errors (not for any objects, just for errors), the `name` property is exactly the name of the constructor. And `message` is taken from the argument.
For instance:
```js run
let error = new Error("Things happen :k");
alert(error.name); // Error
alert(error.message); // Things happen :k
```
Let's see what kind of error `JSON.parse` generates:
```js run
try {
JSON.parse("{ bad json o_O }");
} catch(e) {
*!*
alert(e.name); // SyntaxError
*/!*
alert(e.message); // Unexpected token o in JSON at position 0
}
```
As we can see, that's a `SyntaxError`.
...And in our case, the absense of `name` can be treated as a syntax error also, assuming that users follow a sort of "schema" that requires the existance of `"name"`.
So let's throw it:
```js run
let json = '{ "age": 30 }'; // incomplete data
try {
let user = JSON.parse(json); // <-- no errors
if (!user.name) {
*!*
throw new SyntaxError("Incomplete data: no name"); // (*)
*/!*
}
alert( user.name );
} catch(e) {
alert( "JSON Error: " + e.message ); // JSON Error: Incomplete data: no name
}
```
In the line `(*)` the `throw` operator generates `SyntaxError` with the given `message`, the same way as Javascript would generate itself. The execution of `try` immediately stops and the control flow jumps into `catch`.
Now `catch` became a single place for all error handling: both for `JSON.parse` and other cases.
## Rethrowing
In the example above we implemented error handling for incorrect data. But is it possible that another unexpected error happens in `try {...}` block?
Of course, it is! Normally, a code is a bag with errors. It's typical that even in an open-source utility like `ssh` that is used by millions for decades -- suddenly a crazy bug is discovered that leads to terrible hacks. Not to mention other similar cases.
In our case, `catch` block is meant to process "incorrect data" errors. But right now it catches everything.
For instance, say, we made a programming error, a mistype:
```js run
try {
// ...
JSON.papaparse(); // a mistype, no such function
} catch(e) {
alert( "JSON Error: " + e.message ); // JSON Error: JSON.papaparse is not a function
}
```
By nature, `catch` gets all errors from `try`. Here it got an unexpected type of error, but still shows the same `"JSON Error"` message. That's wrong and also makes the code more difficult to debug.
Fortunately, we can find out which error we've got, for instance by its `name`:
```js run
try {
// ...
JSON.papaparse(); // JSON.papaparse is not a function
} catch(e) {
*!*
alert(e.name); // "TypeError" for trying to call undefined property
*/!*
}
```
The rule is simple:
**Catch should only process errors that it knows and throw all others.**
The technique is called "rethrowing":
1. Catch gets all errors.
2. In `catch(e) {...}` block we analyze the error object `e`.
2. If we don't know how to handle it, then do `throw e`.
In the code below, `catch` only handles `SyntaxError`:
```js run
let json = '{ "age": 30 }'; // incomplete data
try {
let user = JSON.parse(json);
if (!user.name) {
throw new SyntaxError("Incomplete data: no name");
}
*!*
blabla(); // unexpected error
*/!*
alert( user.name );
} catch(e) {
*!*
if (e.name == "SyntaxError") {
alert( "JSON Error: " + e.message );
} else {
throw e;
}
*/!*
}
```
The error made inside `catch` block "falls out" of `try..catch` and can be either caught by an outer `try..catch` construct (if exists) or kills the script.
The example below demonstrates how such errors can be caught by one more level of `try..catch`:
```js run
function readData() {
let json = '{ "age": 30 }';
try {
// ...
*!*
blabla(); // error!
*/!*
} catch (e) {
// ...
if (e.name != 'SyntaxError') {
*!*
throw e; // rethrow (don't know how to deal with it)
*/!*
}
}
}
try {
readData();
} catch (e) {
*!*
alert( "External catch got: " + e ); // caught it!
*/!*
}
```
Here `readData` only knows how to handle `SyntaxError`, while the outer `try..catch` knows how to handle everything.
## try..catch..finally
Wait, that's not all.
The `try..catch` construct may have one more code clause: `finally`.
If it exists, it runs in all cases:
- after `try`, if there were no errors,
- after `catch`, if there were errors.
The extended syntax looks like this:
```js
*!*try*/!* {
... try to execute the code ...
} *!*catch*/!*(e) {
... handle errors ...
} *!*finally*/!* {
... execute always ...
}
```
Try to run this?
```js run
try {
alert( 'try' );
if (confirm('Make an error?')) BAD_CODE();
} catch (e) {
alert( 'catch' );
} finally {
alert( 'finally' );
}
```
The code has two variants:
1. If say answer "Yes" to error, then `try -> catch -> finally`.
2. If say "No", then `try -> finally`.
The `finally` clause is often used when we start doing something before `try..catch` and want to finalize it in any case of outcome.
For instance, we want to measure time that a Fibonacci numbers function `fib(n)` takes. Naturally, we can start measuring before it runs and finish afterwards. But what is there's an error during the function call? In particular, the implementation of `fib(n)` in the code below returns an error for negative or non-integer numbers.
The `finally` clause is a great place to finish the measurements no matter how the function finishes.
```js run
let num = +prompt("Enter a positive integer number?", 35)
let diff, result;
function fib(n) {
if (n < 0 || Math.trunc(n) != n) {
throw new Error("Must not be negative, and also an integer.");
}
return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}
let start = Date.now();
try {
result = fib(num);
} catch (e) {
result = 0;
*!*
} finally {
diff = Date.now() - start;
}
*/!*
alert(result || "error occured");
alert( `execution took ${diff}ms` );
```
Here `finally` guarantees that the time will be measured correctly in both situations -- in case of a successful execution of `fib` and in case of an error in it.
You can check that by running the code with `num=35` -- executes normally, `finally` after `try`, and then with `num=-1`, there will be an immediate error, an the execution will take `0ms`.
In other words, there may be two ways to exist from a function: either a `return` or `throw`. The `finally` handles them both.
```smart header="Variables are local to try..catch..finally clauses"
Please note that `result` and `diff` variables in the code above are declared *before* `try..catch`.
Otherwise, if `let` were made inside the `{...}` clause, it would only be visible inside of it.
```
````smart header="`finally` and `return`"
Finally clause works for *any* exit from `try..catch`. That includes the `return` directive.
In the example below, there's a `return` in `try`. In this case, `finally` is executed just before the control returns to the outer code.
```js run
function func() {
try {
return 1;
} catch (e) {
/* ... */
} finally {
*!*
alert( 'finally' );
*/!*
}
}
alert( func() ); // first works alert from finally, and then this one
```
````
````smart header="`try..finally`"
The `try..finally` construct, without `catch` clause, is also useful. We apply it when we don't want to handle errors right here, but want to be sure that processes that we started are finalized.
```js
function func() {
// start doing something that needs completion
try {
// ...
} finally {
// complete that thing even if all dies
}
}
```
In the code above, an error inside `try` always falls out, because there's no `catch`. But `finally` triggers before that.
````
## Global catch
```warn header="Environment-specific"
The information from this section is not a part of the core Javascript.
```
Let's imagine we've got a fatal error outside of `try..catch`, and the script died.
Is there a way to react on it? We may want to log the error, show something to the user (normally he doesn't see the error message) etc.
There is none in the specification, but environments usually provide it, because it's really handy. For instance, Node.JS has [process.on('uncaughtException')](https://nodejs.org/api/process.html#process_event_uncaughtexception) for that. And in the browser we can assign a function to special [window.onerror](mdn:api/GlobalEventHandlers/onerror) property. It will run in case of an uncaught error.
The syntax:
```js
window.onerror = function(message, url, line, col, error) {
// ...
};
```
`message`
: Error message.
`url`
: URL of the script where error happened.
`line`, `col`
: Line and column numbers where error happened.
`error`
: Error object.
For instance:
```html run untrusted refresh height=1
<script>
*!*
window.onerror = function(message, url, line, col, error) {
alert(`${message}\n At ${line}:${col} of ${url}`);
};
*/!*
function readData() {
badFunc(); // wops, something went wrong!
}
readData();
</script>
```
The role of the global handler is usually not to recover the script execution -- that's probably impossible, but to send the error message to developers.
There are also web-services that provide error-logging facilities, like <https://errorception.com> or <http://www.muscula.com>. They give a script with custom `window.onerror` function, and once inserted into a page, it reports about all errors it gets to their server. Afterwards developers can browse them and get notifications on email about fresh errors.
## Summary
The `try..catch` construct allows to handle runtime errors. It literally allows to try running the code and catch errors that may occur in it.
The syntax is:
```js
try {
// run this code
} catch(err) {
// if an error happened, then jump here
// err is the error object
} finally {
// do in any case after try/catch
}
```
There may be no `catch` section or no `finally`, so `try..catch` and `try..finally` are also valid.
Error objects have following properties:
- `message` -- the human-readable error message.
- `name` -- the string with error name (error constructor name).
- `stack` (non-standard) -- the stack at the moment of error creation.
We can also generate our own errors using the `throw` operator. Technically, the argument of `throw` can be anything, but usually it's an error object inheriting from the built-in `Error` class. More on extending errors in the next chapter.
Rethrowing is a basic pattern of error handling: a `catch` block usually expects and knows how to handle the particular error type, so it should rethrow errors it doesn't know.
Even if we don't have `try..catch`, most environments allow to setup a "global" error handler to catch errors that "fall out". In-browser that's `window.onerror`.

View file

@ -0,0 +1,16 @@
```js run untrusted
class FormatError extends SyntaxError {
constructor(message) {
super(message);
this.name = "FormatError";
}
}
let err = new FormatError("formatting error");
alert( err.message ); // formatting error
alert( err.name ); // FormatError
alert( err.stack ); // stack
alert( err instanceof SyntaxError ); // true
```

View file

@ -0,0 +1,18 @@
importance: 5
---
# Inherit from SyntaxError
Create an error `FormatError` that inherits from `SyntaxError`.
Usage example:
```js
let err = new FormatError("formatting error");
alert( err.message ); // formatting error
alert( err.name ); // FormatError
alert( err.stack ); // stack
alert( err instanceof SyntaxError ); // true
```

View file

@ -0,0 +1,281 @@
# Custom errors, extending Error
When we develop our software, we need our own error classes. For network operations we may need `HttpError`, for database operations `DbError`, for searching operations `NotFoundError` and so on.
Our errors should inherit from with the basic `Error` class and have basic error properties like `message`, `name` and, preferably, `stack`. But they also may have other properties of their own, e.g. `HttpError` objects may have `statusCode` property, that is `404` for the "page not found" error.
Technically, we can use standalone classes for errors, because Javascript allows to use `throw` with any argument. But if we inherit from `Error`, then we can use `obj instanceof Error` check to identify error objects. So it's better to inherit from it.
As we build our application, our own errors naturally form a hierarchy, for instance `HttpTimeoutError` may inherit from `HttpError`. Examples will follow soon.
## Extending Error
As an example, let's create a function `readUser(json)` that should read JSON with user data. We are getting that data from a remote server or, maybe it may be altered by a visitor, or just for the sheer safety -- we should to be aware of possible errors in `json`.
Here's an example of how a valid `json` may look:
```js
let json = `{ "name": "John", "age": 30 }`;
```
If the function receives malformed `json`, then it should throw `SyntaxError`. Fortunately, `JSON.parse` does exactly that.
...But if the `json` is correct, that doesn't mean it has all the data. For instance, if may not have `name` or `age`.
That's called "data validation" -- we need to ensure that the data has all the necessary fields. And if the validation fails, then throwing `SyntaxError` would be wrong, because the data is syntactically correct. So we should throw `ValidationError` -- the error object of our own with the proper message and, preferable, with additional information about the offending field.
Let's make the `ValidationError` class. But to better understand what we're extending -- here's the approximate code for built-in [Error class](https://tc39.github.io/ecma262/#sec-error-message):
```js
class Error {
constructor(message) {
this.message = message;
this.name = "Error"; // (different names for different built-in errors)
this.stack = <sequence of nested calls>; // non-standard! most environments support it
}
}
```
Now let's inherit from it:
```js run untrusted
class ValidationError extends Error {
constructor(message) {
super(message); // (1)
this.name = "ValidationError"; // (2)
}
}
function test() {
throw new ValidationError("Wops!");
}
try {
test();
} catch(err) {
alert(err.message); // Wops!
alert(err.name); // ValidationError
alert(err.stack); // a list of nested calls with line numbers for each
}
```
Notes:
1. In the line `(1)` we call the parent constructor to set the message. Javascript requires us to call `super` in the child constructor anyway.
2. The `name` property for our own errors should be assigned manually, otherwise it would be set by the superclass (to `"Error"`).
Let's try to use it in `readUser(json)`:
```js run
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
// Usage
function readUser(json) {
let user = JSON.parse(json);
if (!user.age) {
throw new ValidationError("No field: age");
}
if (!user.name) {
throw new ValidationError("No field: name");
}
return user;
}
// Working example with try..catch
try {
let user = readUser('{ "age": 25 }');
} catch (err) {
if (err instanceof ValidationError) {
*!*
alert("Invalid data: " + err.message); // Invalid data: No field: name
*/!*
} else if (err instanceof SyntaxError) {
alert("JSON Syntax Error: " + err.message);
} else {
throw err; // unknown error, rethrow it
}
}
```
Everything works -- both our `ValidationError` and the built-in `SyntaxError` from `JSON.parse` are correctly handled.
Please note how the code check for the error type in `catch (err) { ... }`. We could use `if (err.name == "ValidationError")`, but `if (err instanceof ValidationError)` is much better, because in the future we are going to extend `ValidationError`, make new subtypes of it, namely `PropertyRequiredError`. And `instanceof` check will continue to work. So that's future proof.
Also it's important that if we meet an unknown error, then we just rethrow it. The `catch` only knows how to handle validation and syntax errors, other kinds (due to a typo in the code or such) should fall through.
## Further inheritance
The `ValidationError` class is very generic. Let's make a more concrete class `PropertyRequiredError`, exactly for the absent properties. It will carry additional information about the property that's missing.
```js run
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
*!*
class PropertyRequiredError extends ValidationError {
constructor(property) {
super("No property: " + property);
this.name = "PropertyRequiredError";
this.property = property;
}
}
*/!*
// Usage
function readUser(json) {
let user = JSON.parse(json);
if (!user.age) {
throw new PropertyRequiredError("age");
}
if (!user.name) {
throw new PropertyRequiredError("name");
}
return user;
}
// Working example with try..catch
try {
let user = readUser('{ "age": 25 }');
} catch (err) {
if (err instanceof ValidationError) {
*!*
alert("Invalid data: " + err.message); // Invalid data: No property: name
alert(err.name); // PropertyRequiredError
alert(err.property); // name
*/!*
} else if (err instanceof SyntaxError) {
alert("JSON Syntax Error: " + err.message);
} else {
throw err; // unknown error, rethrow it
}
}
```
The new class `PropertyRequiredError` is easier to use, because we just pass the property name to it: `new PropertyRequiredError(property)`. The human-readable `message` is generated by the constructor.
Plese note that `this.name` in `PropertyRequiredError` once again assigned manually. We could evade that by using `this.constructor.name` for `this.name` in the superclass.
The generic solution would be to make `MyError` class that takes care of it, and inherit from it.
For instance:
```js run
class MyError extends Error {
constructor(message) {
super(message);
*!*
this.name = this.constructor.name;
*/!*
}
}
class ValidationError extends MyError { }
class PropertyRequiredError extends ValidationError {
constructor(property) {
super("No property: " + property);
this.property = property;
}
}
alert( new PropertyRequiredError("field").name ); // PropertyRequiredError
```
## Wrapping exceptions
The purpose of the function `readUser` in the code above is -- to "read the user data", right? There may occur different kinds of errors in the process, not only `SyntaxError` and `ValidationError`, but probably others if we continue developing it.
Right now the code which calls `readUser` uses multiple `if` in `catch` to check for different error types and rethrow if the error is unknown.
But the important questions is: do we want to check for all these types every time we call `readUser`?
Often the answer is: "No". The outer code wants to be "one level above all that". It wants to have some kind of "data reading error", and why exactly it happened -- is usually irrelevant (the message has the info). Or, even better if there is a way to get more details, but only if it wants to.
In our case, when a data-reading error occurs, we will create an object of the new class `ReadError`, that will provide the proper message. And we'll also keep the original error in its `cause` property, just in case.
```js run
class ReadError extends Error {
constructor(message, cause) {
super(message);
this.cause = cause;
this.name = 'ReadError';
}
}
class ValidationError extends Error { /*...*/ }
class PropertyRequiredError extends ValidationError { /* ... */ }
function validateUser(user) {
if (!user.age) {
throw new PropertyRequiredError("age");
}
if (!user.name) {
throw new PropertyRequiredError("name");
}
}
function readUser(json) {
let user;
try {
user = JSON.parse(json);
} catch (err) {
if (err instanceof SyntaxError) {
throw new ReadError("Syntax Error", err);
} else {
throw err;
}
}
try {
validateUser(user);
} catch (err) {
if (err instanceof ValidationError) {
throw new ReadError("Validation Error", err);
} else {
throw err;
}
}
}
try {
readUser('{bad json}');
} catch (e) {
if (e instanceof ReadError) {
*!*
alert(e);
// Original error: SyntaxError: Unexpected token b in JSON at position 1
alert("Original error: " + e.cause);
*/!*
} else {
throw e;
}
}
```
The approach is called "wrapping exceptions", because we take "low level exceptions" and "wrap" them into `ReadError` that is more abstract and more convenient to use for the calling code.
## Summary
- We can inherit from `Error` and other built-in error classes normally, just need to take care of `name` property and don't forget to call `super`.
- Most of the time, we should use `instanceof` to check for particular errors. It also works with inheritance. But sometimes we have an error object coming from the 3rd-party library and there's no easy way to get the class. Then `name` property can be used for such checks.
- Wrapping exceptions is a widespread technique when a function handles low-level exceptions and makes a higher-level object to report about the errors. Low-level exceptions sometimes become properties of that object like `err.cause` in the examples above, but that's not strictly required.

View file

@ -0,0 +1 @@
# Error handling