No matter how proficient we are at programming, scripting errors are inevitable. It could be our error, or unusual input, a bad server-side response, and countless other reasons.
Normally, the script stops immediately when an error is sent, printing to the console.
But the
try...catch
syntax structure catches errors and can do more than just stop.Therefore, it is recommended to use Fundebug for online monitoring, and find errors at the first time.
"try...catch" syntax
try...catch
The structure has two statement blocks, namely try
, then catch
:
try {
// code...
} catch (err) {
// error handling
}
The workflow is as follows:
- First
try{...}
the code block executes. - Ignored if there are no errors
catch(err)
: When execution reaches thetry
end, skip thecatch
block. - If an error occurs, the
try
execution of the block is stopped and the flow of control is enteredcatch(err)
.err
The variable (can be any name) contains the information object related to the occurrence of the error.
So, try{...}
an error inside the block doesn't stop the script: we have a chance catch
to handle it inside the block. Let's see more examples.
-
Error free example: show
alert (1)
and(2)
:try {
alert(‘Start of try runs’); // (1) <–
// …no errors here
alert(‘End of try runs’); // (2) <–
} catch(err) {
alert(‘Catch is ignored, because there are no errors’); // (3)
}
alert(“…Then the execution continues”);
-
Example with error: show
(1)
and(3)
:try {
alert(‘Start of try runs’); // (1) <–
lalala; // error, variable is not defined!
alert(‘End of try (never reached)’); // (2)
} catch(err) {
alert(
Error has occured!
); // (3) <–}
alert(“…Then the execution continues”);
try..catch only works on runtime errors
Yes try..catch
, the code must be runtime, in other words, must be valid Javascript code.
If the code syntax error will not work, for example, the following will not catch the curly brace error:
try {
{{{{{{{{{{{{
} catch(e) {
alert("The engine can't understand this code, it's invalid");
}
The Javascript engine reads the code first, then runs it. Errors that occur during the read phase are called "parse-time" errors and are not recoverable because the engine does not understand the code.
So errors that try...catch
can only be handled in valid code are called "runtime" errors, and sometimes "exceptions".
try..catch executes synchronously
If an exception occurs in scheduled code, e.g. setTimeout
, then try...catch
do not catch the exception:
try {
setTimeout(function() {
noSuchVariable; // script will die here
}, 1000);
} catch (e) {
alert( "won't work" );
}
Because it try...catch
wraps the setTimeout
call to the scheduled function. But the function will be delayed execution, at this time the engine has been try...catch
structured immediately.
In order to catch exceptions inside the intended execution function, inside the try...catch
function:
setTimeout(function() {
try {
noSuchVariable; // try..catch handles the error!
} catch (e) {
alert( "error is caught here!" );
}
}, 1000);
error object
When an error occurs, Javascript generates an object containing the details and passes it as a parameter to the catch
block:
try {
// ...
} catch(err) { // <-- the "error object", could use another word instead of err
// ...
}
For all built-in errors, catch
the inner error object has two main properties:
name
is the wrong name, an undefined variable is “ReferenceError”
.
message
Text description of the error message.
There are other non-standard properties in most environments, one that is widely used and supported is: stack
Current call stack: about the nested call sequence that caused the error, for debugging purposes.
Example:
try {
lalala; // error, variable is not defined!
} catch(err) {
alert(err.name); // ReferenceError
alert(err.message); // lalala is not defined
alert(err.stack); // ReferenceError: lalala is not defined at ...
// Can also show an error as a whole
// The error is converted to string as "name: message"
alert(err); // ReferenceError: lalala is not defined
}
Use "try...catch"
Let's explore a real use case:
We know that Javascript supports methods JSON.parse(str)
for reading json values. Typically used to parse json data received from the network, such as server-side or other sources. Receive and call JSON.parse
, as follows:
let json = '{"name":"John", "age": 30}'; // data from the server
let user = JSON.parse(json); // convert the text representation to JS object
// now user is an object with properties from the string
alert( user.name ); // John
alert( user.age ); // 30
If json
non-standard, JSON.parse produces an error and the script stops. Will we be satisfied? of course not!
If the data has some kind of error, the user has absolutely no idea what to send (unless the dev console is opened). Nobody likes a script that stops without any error message when an error occurs.
Let's try...catch
handle errors using:
let json = "{ bad json }";
try {
let user = JSON.parse(json); // <-- when an error occurs...
alert( user.name ); // doesn't work
} catch (e) {
// ...the execution jumps here
alert( "Our apologies, the data has errors, we'll try to request it one more time." );
alert( e.name );
alert( e.message );
}
Here we use catch
blocks to just display information, but can do more: new network requests, suggest an alternative, send error messages to the log, etc., all better than just stopping the code.
throw our own mistakes
What if the json syntax is working, but doesn't have the required name attribute?
as follows:
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" );
}
This JSON.parse
works fine, but the name attribute is missing, which is actually an error for us. To unify error handling, we need to use throw
operations.
Throw operation
This operation produces an error. grammar:
throw <error object>
Technically, anything can be used as an error object. Can be a primitive type such as a number or string, but it is better to use an object, with name and message properties (compatible with the built-in error object).
Javascript has many built-in standard error constructors: Error, SyntaxError, ReferenceError, TypeError, and others. We can also use it to create error objects.
grammar:
let error = new Error(message);
// or
let error = new SyntaxError(message);
let error = new ReferenceError(message);
// ...
For built-in error objects (error objects only), the name property is exactly the name of the constructor and message is the constructor parameter.
let error = new Error("Things happen o_O");
alert(error.name); // Error
alert(error.message); // Things happen o_O
Let's see JSON.parse
this error generated:
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, the error is: SyntaxError
.
In our example, the default name attribute, which can also be considered a syntax error, assumes that users must have a name attribute, so we throw an error:
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
}
On the asterisked line, the throw
operation produces SyntaxError
an error, with the given message
, consistent with the error generated by Javascript itself. Execution in the try block stops immediately and the flow of control jumps to the catch
block.
It's now catch
a block that handles all errors independently: JSON.parse
and other errors.
throws the error again
In the above example, we use try...catch
handle incorrect data, but it is also possible that other exceptions occur in the try...catch
block like variable undefined or other, not only "incorrect data".
as follows:
let json = '{ "age": 30 }'; // incomplete data
try {
user = JSON.parse(json); // <-- forgot to put "let" before user
// ...
} catch(err) {
alert("JSON Error: " + err); // JSON Error: ReferenceError: user is not defined
// (not JSON Error actually)
}
Of course everything is possible! Errors made by programmers, even by heavily used open source tools - can suddenly find a crazy bug that leads to horrific hacks (as happens with ssh tools).
In our example, this try...catch
is to handle incorrect data errors, but in practice, catch catches all errors in the try block. If there is an exception error, the "JSON Error" message is still displayed, such errors also make the code more difficult to debug.
Fortunately, we can find out what type of error is caught, for example, from its name property:
try {
user = { /*...*/ };
} catch(e) {
alert(e.name); // "ReferenceError" for accessing an undefined variable
}
The rules are simple.
Only known errors should be handled, other errors should be re-throwed.
The detailed re-throw errors are explained below:
- catch all errors
- In the
catch(err){...}
block we analyze the error objecterr
- If you don't know how to handle it, then by
throw err
throwing an error
In the code below, we use to rethrow the error so that it catch
only handles SyntaxError
:
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; // rethrow (*)
}
}
catch
An asterisk line inside a block throws an error, which can be caught by the outer struct try...catch
block (if it exists), or simply stop the script.
So the catch
block actually only handles known errors and ignores all other errors.
The following example demonstrates that such errors are try...catch
handled by multi-level blocks.
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!
}
This readData
only knows how to handle SyntaxError errors, and the external ones try...catch
know how to handle any errors.
try…catch…finally
Wait, it's not over yet.
A structure try...catch
can have multiple code clauses: finally
, if present, all cases are executed.
try
After that, if there is no error conditioncatch
After that, if there is an error
The extension syntax looks like this:
try {
... try to execute the code ...
} catch(e) {
... handle errors ...
} finally {
... execute always ...
}
Please try running the code below:
try {
alert( 'try' );
if (confirm('Make an error?')) BAD_CODE();
} catch (e) {
alert( 'catch' );
} finally {
alert( 'finally' );
}
The code has two execution paths:
- If answering "Yes" produces an error, the execution path is
try->catch->finally
. - If the callback is "No", then the path is
try->finally
.
Clauses finally
are usually used when try...catch
you start doing something before a block and need to terminate regardless of the result.
For example, we want to measure the fib(n)
execution time of the Fibonacci function. Naturally, we need to measure before and after execution. But what if there is an error during the function call? In particular, the implementation of fib(n) in the code below will return an error for negative or non-integer numbers.
No matter what happens, clauses finally
are great for completing time measurements.
finally
Responsible for testing execution time in two scenarios - successful fib
function execution and error conditions:
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` );
You can enter 35 according to the prompt to check that the code is running - the execution is normal, and then the try is executed finally. Enter -1 again, an error occurs immediately, the execution takes 0ms, and the time measurement is completed correctly.
In other words, there are two ways to exit a function: either return, or throw an error. finally subhandles are processed.
Variables in try...catch...finally blocks are local variables
Note that in the above code result
and diff
variables, they are try...catch
declared before the block.
Otherwise, if you use let inside a {...} block, it will only be visible inside the block.
finally and return
The finally clause is executed no matter how the try...catch
block ends. Including the displayed return method to return.
The following example, with return in try, in this case finally is executed before control returns to the outer code.
function func() {
try {
return 1;
} catch (e) {
/* ... */
} finally {
alert( 'finally' );
}
}
alert( func() ); // first works alert from finally, and then this one
try...finally
try...finally constructs, without catch
clauses, are also useful. Applies when we don't want to handle errors here, but make sure the start and end processes are executed.
function func() {
// start doing something that needs completion (like measurements)
try {
// ...
} finally {
// complete that thing even if all dies
}
}
In the above code, the error of the try block will always happen because there is no catch block, but the finally will execute before the execution flow jumps outside.
global error catch
The information in this section of the environment specification
is not part of core JavaScript.
We imagine try...catch
that there is a fatal error outside the block, and the code stops immediately. Like a programming error or something worse.
Is there a way to deal with such events? We might want to log an error, show some information to the user (usually he won't see the error message), etc.
Not covered in the Javascript spec, but environments usually provide implementations because they do. For example, Node.JS has process.on("uncaughtException"), and you can assign a function to window.onerror in the browser. It will run without catching errors.
grammar:
window.onerror = function(message, url, line, col, error) {
// ...
};
message
error message
url
error script url
line, col
The error occurs in the number of lines, columns in the code
error
error object
<script>
window.onerror = function(message, url, line, col, error) {
alert(`${message}\n At ${line}:${col} of ${url}`);
};
function readData() {
badFunc(); // Whoops, something went wrong!
}
readData();
</script>
The role of global error handling window.error
generally cannot resume script execution, which is not possible in the case of programming errors, but will send error messages to the developer.
There are also web services that provide error logging for this situation, such as https://errorception.com or http://www.muscula.com .
The workflow is as follows:
- Register the service, and then get a JS script to insert into the page.
- There are custom
window.error
functions in the JS script. - When an error occurs, a network request is sent to the server.
- We can log into the web interface of the service to view the error message.
Summarize
Structs try...catch
can handle runtime errors, literally trying to run the code and then catching errors that might occur.
The syntax is:
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 also be no catch
or finally
block, so try...catch
and try...finally
are both valid syntax.
Error objects have the following properties:
message
- User understandable error messages.name
-- Error name string (error constructor name).stack
--Non-standard --The stack message where the error occurred.
We can also throw
generate our own errors by using operations, which technically throw
can be of any type, but are usually Error
objects inherited from the built-in error classes. Extended error objects are described later.
Rethrowing is the basic pattern of error handling: catch
a block usually expects and knows how to handle a particular error, so it should rethrow unknown errors.
Even if we don't use try...catch
it, most environments support setting global error handling to catch all errors that occur, the browser's built-in window.onerror
.
Original text: https://blog.csdn.net/neweastsun/article/details/76358623
you may be interested in