You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/10-error-handling/1-try-catch/article.md
+58-58Lines changed: 58 additions & 58 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,10 +1,10 @@
1
1
# Error handling, "try..catch"
2
2
3
-
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.
3
+
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 other reasons.
4
4
5
5
Usually, a script "dies" (immediately stops) in case of an error, printing it to console.
6
6
7
-
But there's a syntax construct `try..catch` that allows to "catch" errors and, instead of dying, do something more reasonable.
7
+
But there's a syntax construct `try..catch` that allows us to "catch" errors so the script can, instead of dying, do something more reasonable.
8
8
9
9
## The "try..catch" syntax
10
10
@@ -25,14 +25,14 @@ try{
25
25
It works like this:
26
26
27
27
1. First, the code in `try{...}` is executed.
28
-
2. If there were no errors, then `catch(err)` is ignored: the execution reaches the end of `try` and then jumps over`catch`.
29
-
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.
28
+
2. If there were no errors, then `catch(err)` is ignored: the execution reaches the end of `try` and goes on, skipping`catch`.
29
+
3. If an error occurs, then the `try` execution is stopped, and control flows to the beginning of `catch(err)`. The `err` variable (we can use any name for it) will contain an error object with details about what happened.
30
30
31
31

32
32
33
-
So, an error inside the `try{…}` block does not kill the script: we have a chance to handle it in `catch`.
33
+
So, an error inside the `try{…}` block does not kill the script -- we have a chance to handle it in `catch`.
34
34
35
-
Let's see more examples.
35
+
Let's look at some examples.
36
36
37
37
- An errorless example: shows `alert``(1)` and `(2)`:
38
38
@@ -50,8 +50,6 @@ Let's see more examples.
50
50
alert('Catch is ignored, because there are no errors'); // (3)
51
51
52
52
}
53
-
54
-
alert("...Then the execution continues");
55
53
```
56
54
- An example with an error: shows `(1)` and `(3)`:
57
55
@@ -68,11 +66,9 @@ Let's see more examples.
68
66
69
67
} catch(err){
70
68
71
-
alert(`Error has occured!`); // *!*(3) <--*/!*
69
+
alert(`Error has occurred!`); // *!*(3) <--*/!*
72
70
73
71
}
74
-
75
-
alert("...Then the execution continues");
76
72
```
77
73
78
74
@@ -89,9 +85,9 @@ try{
89
85
}
90
86
```
91
87
92
-
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.
88
+
The JavaScript engine first reads the code, and then runs it. The errors that occur on the reading phase are called "parse-time" errors and are unrecoverable (from inside that code). That's because the engine can't understand the code.
93
89
94
-
So, `try..catch` can only handle errors that occur in the valid code. Such errors are called "runtime errors" or, sometimes, "exceptions".
90
+
So, `try..catch` can only handle errors that occur in valid code. Such errors are called "runtime errors" or, sometimes, "exceptions".
95
91
````
96
92
97
93
@@ -108,7 +104,7 @@ try{
108
104
}
109
105
```
110
106
111
-
That's because `try..catch` actually wraps the `setTimeout` call that schedules the function. But the function itself is executed later, when the engine has already left the `try..catch` construct.
107
+
That's because the function itself is executed later, when the engine has already left the `try..catch` construct.
112
108
113
109
To catch an exception inside a scheduled function, `try..catch` must be inside that function:
114
110
```js run
@@ -134,10 +130,10 @@ try{
134
130
}
135
131
```
136
132
137
-
For all built-in errors, the error object inside `catch` block has two main properties:
133
+
For all built-in errors, the error object has two main properties:
138
134
139
135
`name`
140
-
: Error name. For an undefined variable that's `"ReferenceError"`.
136
+
: Error name. For instance, for an undefined variable that's `"ReferenceError"`.
141
137
142
138
`message`
143
139
: Textual message about error details.
@@ -157,7 +153,7 @@ try{
157
153
} catch(err){
158
154
alert(err.name); // ReferenceError
159
155
alert(err.message); // lalala is not defined
160
-
alert(err.stack); // ReferenceError: lalala is not defined at ...
156
+
alert(err.stack); // ReferenceError: lalala is not defined at (...call stack)
161
157
162
158
// Can also show an error as a whole
163
159
// The error is converted to string as "name: message"
@@ -174,8 +170,8 @@ If we don't need error details, `catch` may omit it:
174
170
```js
175
171
try{
176
172
// ...
177
-
} catch{
178
-
// error object omitted
173
+
} catch{ // <-- without (err)
174
+
// ...
179
175
}
180
176
```
181
177
@@ -187,7 +183,7 @@ As we already know, JavaScript supports the [JSON.parse(str)](mdn:js/JSON/parse)
187
183
188
184
Usually it's used to decode data received over the network, from the server or another source.
189
185
190
-
We receive it and call `JSON.parse`, like this:
186
+
We receive it and call `JSON.parse` like this:
191
187
192
188
```js run
193
189
let json = '{"name":"John", "age":30}'; // data from the server
@@ -205,7 +201,7 @@ You can find more detailed information about JSON in the <info:json> chapter.
205
201
206
202
**If `json` is malformed, `JSON.parse` generates an error, so the script "dies".**
207
203
208
-
Should we be satisfied with that? Of course, not!
204
+
Should we be satisfied with that? Of course not!
209
205
210
206
This way, if something's wrong with the data, the visitor will never know that (unless they open the developer console). And people really don't like when something "just dies" without any error message.
211
207
@@ -302,13 +298,13 @@ try{
302
298
*!*
303
299
alert(e.name); // SyntaxError
304
300
*/!*
305
-
alert(e.message); // Unexpected token o in JSON at position 0
301
+
alert(e.message); // Unexpected token b in JSON at position 2
306
302
}
307
303
```
308
304
309
305
As we can see, that's a `SyntaxError`.
310
306
311
-
And in our case, the absence of `name` could be treated as a syntax error also, assuming that users must have a `name`.
307
+
And in our case, the absence of `name` is an error, as users must have a `name`.
312
308
313
309
So let's throw it:
314
310
@@ -338,9 +334,9 @@ Now `catch` became a single place for all error handling: both for `JSON.parse`
338
334
339
335
## Rethrowing
340
336
341
-
In the example above we use `try..catch` to handle incorrect data. But is it possible that *another unexpected error* occurs within the `try{...}` block? Like a variable is undefined or something else, not just that"incorrect data" thing.
337
+
In the example above we use `try..catch` to handle incorrect data. But is it possible that *another unexpected error* occurs within the `try{...}` block? Like a programming error (variable is not defined) or something else, not just this"incorrect data" thing.
342
338
343
-
Like this:
339
+
For example:
344
340
345
341
```js run
346
342
let json = '{"age": 30 }' // incomplete data
@@ -355,31 +351,35 @@ try{
355
351
}
356
352
```
357
353
358
-
Of course, everything's possible! Programmers do make mistakes. Even in open-source utilities used by millions for decades -- suddenly a crazy bug may be discovered that leads to terrible hacks (like it happened with the `ssh` tool).
354
+
Of course, everything's possible! Programmers do make mistakes. Even in open-source utilities used by millions for decades -- suddenly a bug may be discovered that leads to terrible hacks.
355
+
356
+
In our case, `try..catch` is placed to catch "incorrect data" errors. But by its nature, `catch` gets *all* errors from `try`. Here it gets an unexpected error, but still shows the same `"JSONError"` message. That's wrong and also makes the code more difficult to debug.
357
+
358
+
To avoid such problems, we can employ the "rethrowing" technique. The rule is simple:
359
+
360
+
**Catch should only process errors that it knows and "rethrow" all others.**
361
+
362
+
The "rethrowing" technique can be explained in more detail as:
359
363
360
-
In our case, `try..catch` is meant to catch "incorrect data" errors. But by its nature, `catch` gets *all* errors from `try`. Here it gets an unexpected error, but still shows the same `"JSONError"` message. That's wrong and also makes the code more difficult to debug.
364
+
1. Catch gets all errors.
365
+
2. In the `catch(err){...}` block we analyze the error object `err`.
366
+
2. If we don't know how to handle it, we do `throw err`.
361
367
362
-
Fortunately, we can find out which error we get, for instance from its `name`:
368
+
Usually, we can check the error type using the `instanceof` operator:
363
369
364
370
```js run
365
371
try{
366
372
user ={/*...*/ };
367
-
} catch(e){
373
+
} catch(err){
368
374
*!*
369
-
alert(e.name); // "ReferenceError" for accessing an undefined variable
375
+
if (err instanceof ReferenceError){
370
376
*/!*
377
+
alert('ReferenceError'); // "ReferenceError" for accessing an undefined variable
378
+
}
371
379
}
372
380
```
373
381
374
-
The rule is simple:
375
-
376
-
**Catch should only process errors that it knows and "rethrow" all others.**
377
-
378
-
The "rethrowing" technique can be explained in more detail as:
379
-
380
-
1. Catch gets all errors.
381
-
2. In `catch(err){...}` block we analyze the error object `err`.
382
-
2. If we don't know how to handle it, then we do `throw err`.
382
+
We can also get the error class name from `err.name` property. All native errors have it. Another option is to read `err.constructor.name`.
383
383
384
384
In the code below, we use rethrowing so that `catch` only handles `SyntaxError`:
385
385
@@ -402,7 +402,7 @@ try{
402
402
} catch(e){
403
403
404
404
*!*
405
-
if (e.name == "SyntaxError"){
405
+
if (e instanceof SyntaxError){
406
406
alert( "JSONError:" + e.message );
407
407
} else{
408
408
throw e; // rethrow (*)
@@ -429,7 +429,7 @@ function readData(){
429
429
*/!*
430
430
} catch (e){
431
431
// ...
432
-
if (e.name != 'SyntaxError'){
432
+
if (!(e instanceof SyntaxError)){
433
433
*!*
434
434
throw e; // rethrow (don't know how to deal with it)
435
435
*/!*
@@ -489,7 +489,7 @@ The code has two ways of execution:
489
489
1. If you answer "Yes" to "Make an error?", then `try -> catch -> finally`.
490
490
2. If you say "No", then `try -> finally`.
491
491
492
-
The `finally` clause is often used when we start doing something before `try..catch` and want to finalize it in any case of outcome.
492
+
The `finally` clause is often used when we start doing something and want to finalize it in any case of outcome.
493
493
494
494
For instance, we want to measure the time that a Fibonacci numbers function `fib(n)` takes. Naturally, we can start measuring before it runs and finish afterwards. But what if 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.
495
495
@@ -521,20 +521,20 @@ try{
521
521
}
522
522
*/!*
523
523
524
-
alert(result || "error occured");
524
+
alert(result || "error occurred");
525
525
526
526
alert( `execution took ${diff}ms` );
527
527
```
528
528
529
-
You can check by running the code with entering `35` into `prompt` -- it executes normally, `finally` after `try`. And then enter `-1` -- there will be an immediate error, an the execution will take `0ms`. Both measurements are done correctly.
529
+
You can check by running the code with entering `35` into `prompt` -- it executes normally, `finally` after `try`. And then enter `-1` -- there will be an immediate error, and the execution will take `0ms`. Both measurements are done correctly.
530
530
531
-
In other words, there may be two ways to exit a function: either a `return` or `throw`. The `finally` clause handles them both.
531
+
In other words, the function may finish with `return` or `throw`, that doesn't matter. The `finally` clause executes in both cases.
532
532
533
533
534
534
```smart header="Variables are local inside `try..catch..finally`"
535
535
Please note that `result` and `diff` variables in the code above are declared *before* `try..catch`.
536
536
537
-
Otherwise, if `let` were made inside the `{...}` block, it would only be visible inside of it.
537
+
Otherwise, if we declared `let` in `try` block, it would only be visible inside of it.
538
538
```
539
539
540
540
````smart header="`finally` and `return`"
@@ -565,7 +565,7 @@ alert( func() ); // first works alert from finally, and then this one
565
565
566
566
````smart header="`try..finally`"
567
567
568
-
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.
568
+
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.
569
569
570
570
```js
571
571
function func(){
@@ -577,7 +577,7 @@ function func(){
577
577
}
578
578
}
579
579
```
580
-
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.
580
+
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.
581
581
````
582
582
583
583
## Global catch
@@ -586,11 +586,11 @@ In the code above, an error inside `try` always falls out, because there's no `c
586
586
The information from this section is not a part of the core JavaScript.
587
587
```
588
588
589
-
Let's imagine we've got a fatal error outside of `try..catch`, and the script died. Like a programming error or something else terrible.
589
+
Let's imagine we've got a fatal error outside of `try..catch`, and the script died. Like a programming error or some other terrible thing.
590
590
591
-
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.
591
+
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.
592
592
593
-
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.
593
+
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 the special [window.onerror](mdn:api/GlobalEventHandlers/onerror) property, that will run in case of an uncaught error.
594
594
595
595
The syntax:
596
596
@@ -637,13 +637,13 @@ There are also web-services that provide error-logging for such cases, like <htt
637
637
They work like this:
638
638
639
639
1. We register at the service and get a piece of JS (or a script URL) from them to insert on pages.
640
-
2. That JS script has a custom `window.onerror` function.
640
+
2. That JS script sets a custom `window.onerror` function.
641
641
3. When an error occurs, it sends a network request about it to the service.
642
642
4. We can log in to the service web interface and see errors.
643
643
644
644
## Summary
645
645
646
-
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.
646
+
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.
647
647
648
648
The syntax is:
649
649
@@ -658,18 +658,18 @@ try{
658
658
}
659
659
```
660
660
661
-
There may be no `catch` section or no `finally`, so `try..catch` and `try..finally` are also valid.
661
+
There may be no `catch` section or no `finally`, so shorter constructs `try..catch` and `try..finally` are also valid.
662
662
663
663
Error objects have following properties:
664
664
665
665
- `message` -- the human-readable error message.
666
666
- `name` -- the string with error name (error constructor name).
667
-
- `stack` (non-standard) -- the stack at the moment of error creation.
667
+
- `stack` (non-standard, but well-supported) -- the stack at the moment of error creation.
668
668
669
-
If error is not needed, we can omit it by using `catch{` instead of `catch(err){`.
669
+
If an error object is not needed, we can omit it by using `catch{` instead of `catch(err){`.
670
670
671
671
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.
672
672
673
-
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.
673
+
*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.
674
674
675
-
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`.
675
+
Even if we don't have `try..catch`, most environments allow us to setup a "global" error handler to catch errors that "fall out". In-browser, that's `window.onerror`.
0 commit comments