This commit is contained in:
Ilya Kantor 2019-08-06 16:40:27 +03:00
parent 68fdeeef3f
commit 84ff065501
2 changed files with 18 additions and 22 deletions

View file

@ -44,4 +44,4 @@ function f() {
f(); // 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. It's `finally` that guarantees the cleanup here. If we just put the code at the end of `f`, it wouldn't run in these situations.

View file

@ -25,14 +25,14 @@ try {
It works like this: It works like this:
1. First, the code in `try {...}` is executed. 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`. 2. If there were no errors, then `catch(err)` is ignored: the execution reaches the end of `try` and goes on skipping `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. 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.
![](try-catch-flow.svg) ![](try-catch-flow.svg)
So, an error inside the `try {…}` block does not kill the script: we have a chance to handle it in `catch`. So, an error inside the `try {…}` block does not kill the script: we have a chance to handle it in `catch`.
Let's see more examples. Let's see examples.
- An errorless example: shows `alert` `(1)` and `(2)`: - An errorless example: shows `alert` `(1)` and `(2)`:
@ -50,8 +50,6 @@ Let's see more examples.
alert('Catch is ignored, because there are no errors'); // (3) alert('Catch is ignored, because there are no errors'); // (3)
} }
alert("...Then the execution continues");
``` ```
- An example with an error: shows `(1)` and `(3)`: - An example with an error: shows `(1)` and `(3)`:
@ -71,8 +69,6 @@ Let's see more examples.
alert(`Error has occurred!`); // *!*(3) <--*/!* alert(`Error has occurred!`); // *!*(3) <--*/!*
} }
alert("...Then the execution continues");
``` ```
@ -134,10 +130,10 @@ try {
} }
``` ```
For all built-in errors, the error object inside `catch` block has two main properties: For all built-in errors, the error object has two main properties:
`name` `name`
: Error name. For an undefined variable that's `"ReferenceError"`. : Error name. For instance, for an undefined variable that's `"ReferenceError"`.
`message` `message`
: Textual message about error details. : Textual message about error details.
@ -157,7 +153,7 @@ try {
} catch(err) { } catch(err) {
alert(err.name); // ReferenceError alert(err.name); // ReferenceError
alert(err.message); // lalala is not defined alert(err.message); // lalala is not defined
alert(err.stack); // ReferenceError: lalala is not defined at ... alert(err.stack); // ReferenceError: lalala is not defined at (...call stack)
// Can also show an error as a whole // Can also show an error as a whole
// The error is converted to string as "name: message" // The error is converted to string as "name: message"
@ -174,8 +170,8 @@ If we don't need error details, `catch` may omit it:
```js ```js
try { try {
// ... // ...
} catch { } catch { // <-- without (err)
// error object omitted // ...
} }
``` ```
@ -187,7 +183,7 @@ As we already know, JavaScript supports the [JSON.parse(str)](mdn:js/JSON/parse)
Usually it's used to decode data received over the network, from the server or another source. Usually it's used to decode data received over the network, from the server or another source.
We receive it and call `JSON.parse`, like this: We receive it and call `JSON.parse` like this:
```js run ```js run
let json = '{"name":"John", "age": 30}'; // data from the server let json = '{"name":"John", "age": 30}'; // data from the server
@ -308,7 +304,7 @@ try {
As we can see, that's a `SyntaxError`. As we can see, that's a `SyntaxError`.
And in our case, the absence of `name` could be treated as a syntax error also, assuming that users must have a `name`. And in our case, the absence of `name` is an error, as users must have a `name`.
So let's throw it: So let's throw it:
@ -534,7 +530,7 @@ In other words, the function may finish with `return` or `throw`, that doesn't m
```smart header="Variables are local inside `try..catch..finally`" ```smart header="Variables are local inside `try..catch..finally`"
Please note that `result` and `diff` variables in the code above are declared *before* `try..catch`. Please note that `result` and `diff` variables in the code above are declared *before* `try..catch`.
Otherwise, if `let` were made inside the `{...}` block, it would only be visible inside of it. Otherwise, if we declared `let` in `try` block, it would only be visible inside of it.
``` ```
````smart header="`finally` and `return`" ````smart header="`finally` and `return`"
@ -565,7 +561,7 @@ alert( func() ); // first works alert from finally, and then this one
````smart header="`try..finally`" ````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. The `try..finally` construct, without `catch` clause, is also useful. We apply it when we don't want to handle errors here (let them fall through), but want to be sure that processes that we started are finalized.
```js ```js
function func() { function func() {
@ -577,7 +573,7 @@ function func() {
} }
} }
``` ```
In the code above, an error inside `try` always falls out, because there's no `catch`. But `finally` works before the execution flow jumps outside. In the code above, an error inside `try` always falls out, because there's no `catch`. But `finally` works before the execution flow leaves the function.
```` ````
## Global catch ## Global catch
@ -590,7 +586,7 @@ Let's imagine we've got a fatal error outside of `try..catch`, and the script di
Is there a way to react on such occurrences? We may want to log the error, show something to the user (normally they don't see error messages) etc. Is there a way to react on such occurrences? We may want to log the error, show something to the user (normally they don't see error messages) etc.
There is none in the specification, but environments usually provide it, because it's really useful. 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. There is none in the specification, but environments usually provide it, because it's really useful. 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, that will run in case of an uncaught error.
The syntax: The syntax:
@ -637,7 +633,7 @@ There are also web-services that provide error-logging for such cases, like <htt
They work like this: They work like this:
1. We register at the service and get a piece of JS (or a script URL) from them to insert on pages. 1. We register at the service and get a piece of JS (or a script URL) from them to insert on pages.
2. That JS script has a custom `window.onerror` function. 2. That JS script sets a custom `window.onerror` function.
3. When an error occurs, it sends a network request about it to the service. 3. When an error occurs, it sends a network request about it to the service.
4. We can log in to the service web interface and see errors. 4. We can log in to the service web interface and see errors.
@ -658,18 +654,18 @@ try {
} }
``` ```
There may be no `catch` section or no `finally`, so `try..catch` and `try..finally` are also valid. There may be no `catch` section or no `finally`, so shorter constructs `try..catch` and `try..finally` are also valid.
Error objects have following properties: Error objects have following properties:
- `message` -- the human-readable error message. - `message` -- the human-readable error message.
- `name` -- the string with error name (error constructor name). - `name` -- the string with error name (error constructor name).
- `stack` (non-standard) -- the stack at the moment of error creation. - `stack` (non-standard, but well-supported) -- the stack at the moment of error creation.
If an error object is not needed, we can omit it by using `catch {` instead of `catch(err) {`. If an error object is not needed, we can omit it by using `catch {` instead of `catch(err) {`.
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. 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. *Rethrowing* is a very important 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`. 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`.