Error handling, try..catch
JavaScript Error handling, “try..catch” (JavaScript错误处理, “try.. catch”)
Even the greatest programmers can have errors in the scripts. Generally, errors can occur as a result of mistakes, unexpected user input, and a thousand other reasons. (即使是最伟大的程序员也会在脚本中出现错误。一般来说,错误可能是由于错误、意外的用户输入和成千上万的其他原因造成的。)
But, hopefully, there exists a try..catch syntax construct, allowing to catch errors, so the script can do something more reasonable. (但是,希望存在一个try.. catch语法构造,允许捕获错误,因此脚本可以做一些更合理的事情。)
The syntax of try…catch
The syntax of try…catch (Try… catch的语法)
Two blocks are included in thetry..catch construct: try and catch:
try {
// code
} catch (err) {
// error handling
}
The code works in the following order:
The code is executed in try {…}. catch(err) is ignored if there are no errors. The execution gets to the end of the try and continues, skipping the catch. In case there is an error, the execution of try is stopped. The control flows to the start of catch(err). So, the err variable (any name can be given to it) will involve an error object that includes details about what happened.
The process is illustrated in the picture below:
Let’s check out examples. In the first example, there is no error:
try {
console.log('Start test runs');
// no errors here
(//这里没有错误)
console.log('End test runs');
} catch (err) {
console.log('Catch ignores because there are no errors');
}
In the second example, error detection is demonstrated like this:
try {
console.log('Start test runs');
anyVar; // error, variable isn't defined
console.log('End of test (never reached)');
} catch (err) {
console.log(`Error has occurred`);
}
Note that try..catch works only for runnable codes. That is, it works in the valid JavaScript. (请注意, try.. catch仅适用于可运行的代码。也就是说,它在有效的JavaScript中工作。)
If the code is syntactically wrong. For example:
try {
(((((((((
}
catch (e) {
console.log("The engine does not understand this code, it is invalid");
}
In the example above, you can see unmatched curly braces. (在上面的示例中,您可以看到不匹配的大括号。)
Generally, the engine of JavaScript first reads the code and then runs it. The errors, occurring in the reading phase are known as “parse-time” errors. They are unrecoverable as the engine doesn’t understand the code. (一般来说, JavaScript引擎首先读取代码,然后运行它。在读取阶段发生的错误称为“解析时间”错误。由于引擎无法理解代码,因此它们无法恢复。)
So, try..catch only catches errors happening in a valid code. Errors like this are known as “runtime errors” or “exceptions”. (因此, try.. catch仅捕获有效代码中发生的错误。此类错误称为“运行时错误”或“异常”。)
Error Object
Error Object (客体错误 ,目的物的错误)
Once an error happens, JavaScript creates an object, involving the details about it. Then, the object is passed as an argument to catch, like this:
try {
//...
} catch (err) { //the "error object", could use another word instead of err
}
The error object includes two primary properties for all built-in errors: name (the error name) and message (the textual message about the details of the error).
Other non-standard properties are also available in most environments. One of the most commonly used properties is stack: a string, containing information about the sequence of nested that led to an error. As a rule, developers use it for debugging purposes. Here is an example:
try {
anyVariable; // error, variable isn't defined
} catch (err) {
console.log(err.name); // ReferenceError
console.log(err.message); // anyVariable is not defined
console.log(err.stack); // ReferenceError: anyVariable is not defined at (...call stack)
// May also show an error in general
(//也可能显示一般错误)
console.log(err); // ReferenceError: anyVariable is not defined
}
The Usage of “try…catch”
The Usage of “try…catch” (“try… catch”的用法)
You have already learned from previous chapters that JavaScript supports the JSON.parse(str) method for reading JSON-encoded values. (您已经从前面的章节中了解到, JavaScript支持JSON.parse (str)方法来读取JSON编码的值。)
As a rule, it is used for decoding data, received over the network, either from the server or another source. (通常,它用于解码通过网络从服务器或其他来源接收的数据。)
It is received and called JSON.parse, as follows:
let json = '{"name":"John", "age": 25}'; // data from the server
let user = JSON.parse(json); // convert text view to js object
//now the user is an object with properties from the string
console.log(user.name); // John
console.log(user.age); // 25
In case json is malformed, JSON.parse creates an error, and the script “dies”. To get out of such a situation, you can use try..catch, like this:
let json = "{ bad json }";
try {
let user = JSON.parse(json); // an error occurs..
console.log(user.name); // doesn't work
} catch (e) {
//the execution jumps here
(//执行跳转到这里)
console.log("Our apologize, there are errors in the data, we will try to request them again.");
console.log(e.name);
console.log(e.message);
}
Throwing errors
Throwing errors (抛出错误)
Let’s check out a case when json is syntactically correct but doesn’t include a required name property:
let json = '{ "age": 25 }'; // incomplete data
try {
let user = JSON.parse(json); //no errors
console.log(user.name); // no name
} catch (e) {
console.log("doesn't execute");
}
Although JSON.parse runs normally here, but the absence of the name is considered an error. (虽然JSON.parse在这里正常运行,但缺少名称被认为是一个错误。)
For unifying the error handling process, you can use the throw operator. (为了统一错误处理过程,您可以使用throw运算符。)
“Throw” Operator
The throw operator is used for generating an error. The syntax of the throw operator is the following:
throw <error object>
Technically, anything can be used as an error object. It can even be a primitive such as a number or a string. However, it would be best if you used objects, preferably including name and message properties. (从技术上讲,任何东西都可以用作错误对象。它甚至可以是原语,例如数字或字符串。但是,最好使用对象,最好包括名称和消息属性。)
Also, JavaScript has multiple built-in constructors for standard errors: SyntaxError, Error, ReferenceError, and so on.
Their syntax looks like this:
let error = new Error(message);
// or
let error = new SyntaxError(message);
let error = new ReferenceError(message);
For-built-in errors the name property is the constructor name. The message should be taken from the argument, like this:
let error = new Error("Things happen");
console.log(error.name); // Error
console.log(error.message); // Things happen
JSON.parse generates this kind of error:
try {
JSON.parse("{ bad json }");
} catch (e) {
console.log(e.name); // SyntaxError
console.log(e.message); // Unexpected token b in JSON at position 2
}
So, it’s a SyntaxError. (所以,这是一个SyntaxError。)
Now, let’s see how to throw the error:
let json = '{ "age": 25 }'; // incomplete data
try {
let user = JSON.parse(json); // no errors
if (!user.name) {
throw new SyntaxError("Incomplete data: no name");
}
console.log(user.name);
} catch (e) {
console.log("JSON Error: " + e.message); // JSON Error: Incomplete data: no name
}
Rethrowing
Rethrowing (正在重新抛掷)
Above it was shown how to handle error using try..catch. But, it is possible that another unexpected error occurs inside the try {…} block. let’s check out a case:
let json = '{ "age": 35 }'; // not complete data
try {
user = JSON.parse(json); // missed the "let" before the user
} catch (err) {
console.log("JSON Error: " + err); // JSON Error: ReferenceError: no user is defined
}
So, programmers can make mistakes. Anytime a bug may be found out and lead to hacks. (因此,程序员可能会犯错误。任何时候都可能发现漏洞并导致黑客入侵。)
Luckily, it is possible to find out what error you get, for example from the name, like here:
try {
user = { /*...*/ };
} catch (e) {
console.log(e.name); // "ReferenceError" for accessing an undefined variable
}
The catch is only capable of processing errors that it recognizes and rethrowing others. (捕获只能处理它识别的错误并重新抛出其他错误。)
See how catch handles only SyntaxError in the example below:
let json = '{ "age": 25 }'; // not complete data
try {
let user = JSON.parse(json);
if (!user.name) {
throw new SyntaxError("Incomplete data: no name");
}
anyFunc(); // unexpected error
console.log(user.name);
} catch (e) {
if (e.name == "SyntaxError") {
console.log("JSON Error: " + e.message);
} else {
throw e; // rethrow
}
}
The technique of rethrowing can be explained in the following steps:
All the errors are got by the catch. The error object err is analyzed in the catch(err) {…} block. In case you don’t know how to handle it, it will be thrown.
In the example below, you can see the process of rethrowing in a way that only SyntaxError is handled by the catch. (在下面的示例中,您可以看到以catch仅处理SyntaxError的方式重新抛出的过程。)
let json = '{ "age": 25 }'; // incomplete data
try {
let user = JSON.parse(json);
if (!user.name) {
throw new SyntaxError("Incomplete data: no name");
}
anyFunc(); //error
console.log(user.name);
} catch (e) {
if (e.name == "SyntaxError") {
console.log("JSON Error: " + e.message);
} else {
throw e; // rethrow
}
}
The catch block is capable of catching only the errors that it knows how to work with. So, it skips all the others. (Catch块只能捕获它知道如何处理的错误。所以,它跳过了所有其他的。)
Let’s see another example where errors like that are taught by another level of stry..catch:
function readData() {
let json = '{ "age": 25 }';
try {
//...
anyFunc(); // error
} catch (e) {
//...
if (e.name != 'SyntaxError') {
throw e; // rethrow
}
}
}
try {
readData();
} catch (e) {
console.log("External catch got: " + e);
}
The readData can handle only SyntaxError, while the outer try..catch is able handle anything. (ReadData只能处理SyntaxError ,而外部try.. catch能够处理任何内容。)
Finally
Finally
The construct of try..catch has another code clause, known as finally. (Try.. catch的构造有另一个代码子句,称为finally。)
In case finally is detected, it will execute cases like:
If there were no errors, after try. If there were errors- after the catch. (如果没有错误,请在尝试后再尝试。 如果有错误-在捕获后。)
The extended syntax of finally is the following;
try {
... try to execute the code
}
catch (e) {
...handle errors
}
finally {
...execute always
}
Running this code will look as follows:
try {
console.log('try');
if (confirm('To make an error?')) BAD_CODE();
} catch (e) {
console.log('catch');
} finally {
console.log('finally');
}
The finally clause is generally used when one begins to do something and wishes to finalize it regardless of the outcome. (当一个人开始做某事并希望最终完成某事时,无论结果如何,通常都会使用finally子句。)
Let’s check out an example of using finally in case of successful execution of fib, and in case of an error:
let num = +prompt("Enter a positive integer number?", 20);
let diff, result;
function fib(n) {
if (n < 0 || Math.trunc(n) != n) {
throw new Error("Must not be negative as well as 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;
}
console.log(result || "error occurred");
console.log(`execution took ${diff}ms`);
So, the function can end with return or throw, it doesn’t matter. The finally clause can run in both of the cases. (因此,函数可以以return或throw结尾,这无关紧要。finally子句可以在这两种情况下运行。)
Also, you should take into account that the variables such as result and diff in the code above should be before try..catch. (此外,您应该考虑到上面代码中的result和diff等变量应该在try.. catch之前。)
In case of putting let in the try block, it will be noticeable only inside of it. (如果将let放入try块中,则仅在其内部可见。)
Global Catch
Global Catch (全球捕捉)
The information in this part is not a part of core JavaScript but it can also be useful for you. (本部分中的信息不是核心JavaScript的一部分,但它也可能对您有用。)
For fatal errors occurring outside try..catch, there is another way to deal with them. In browsers, you can appoint a function to the specific window.onerror property, which will execute in case there is an uncaught error. Its syntax will look like this:
window.onerror = function (message, url, line, column, err) {
// ...
};
Here is an example of its usage:
<!DOCTYPE html>
<html>
<title>Title of the document</title>
<head></head>
<body>
<script>
window.onerror = function(message, url, line, column, err) {
alert(`${message}\n At ${line}:${column} of ${url}`);
};
function readData() {
badFn(); // Error, something went wrong!
}
readData();
</script>
</body>
</html>
So, the primary role of the global handler window.onerror is not recovering the execution of the script but sending an error message to the developers. (因此,全局处理程序window.onerror的主要作用不是恢复脚本的执行,而是向开发人员发送错误消息。)
Summary
Summary (概要)
The construct of try..catch allows handling runtime errors. With it, you can run the code and catch errors that might occur. (Try.. catch的构造允许处理运行时错误。使用它,您可以运行代码并捕获可能发生的错误。)
Also, the errors can be generated with the throw operator. As a rule, it is an error object that inherits from the built-in Error class. (此外,抛出运算符可以生成错误。通常,它是继承自内置Error类的error对象。)
Another essential pattern of error handling is rethrowing. Usually, the catch block knows how to handle specific error types, rethrowing errors that it doesn’t recognize. (错误处理的另一个基本模式是重新抛出。通常, catch块知道如何处理特定的错误类型,重新抛出它无法识别的错误。)
Even in the case of not having try..catch, most of the environments allow setting up a global error handler for catching the errors that fall out. But, in case of inheriting, it is possible to use obj instanceof Error for identifying error objects. Hence, it is always better to inherit from it. (即使没有try.. catch ,大多数环境也允许设置全局错误处理程序来捕获掉出来的错误。但是,在继承的情况下,可以使用obj instanceof Error来识别错误对象。因此,从中继承总是更好的。)