JavaScript 中的错误处理

Posted by cl9000 on July 18, 2020

人生不是一种享乐,而是一桩十分沉重的工作。 ——<列夫·托尔斯泰>

作者:Ashish Lahoti
译者:cl9000
来源:https://codingnconcepts.com/javascript/error-handling-in-javascript/

在本教程中,我们将使用学习错误处理 try,catch,finallythrow 语句。我们还将学习内置的 JavaScript 错误对象( Error, SyntaxError, ReferenceError, 等)以及如何定义自定义错误。

1. 使用 try…catch…finally…throw

我们使用 try,catch,finallythrow 关键字在 JavaScript 处理错误。

  • try 块包装您的代码以检查错误。
  • throw 关键字用于抛出自定义错误。
  • catch 块处理捕获的错误。您将 catch块与try块链接起来。
  • finally 的代码块不管结果始终的执行。您用 trycatch块链接finally

1.1. try

每个 try 块必须与catchfinally块中的至少一个链接在一起,否则将引发 SyntaxError

让我们try单独使用块来验证:

1
2
3
try {
throw new Error('Error while executing the code');
}

ⓧ Uncaught SyntaxError: Missing catch or finally after try

1.2. try…catch

推荐使用 trycatch 块,处理由优雅抛出的错误 try 块。

1
2
3
4
5
try {
throw new Error('Error while executing the code');
} catch (err) {
console.error(err.message);
}

➤ ⓧ Error while executing the code

1.2.1 无效代码使用 try…catch

try..catch无法捕捉的无效的JavaScript代码的例外,例如下面的代码try块在语法上是错误的,不能被捕获catch块。

1
2
3
4
5
try {
~!$%^&*
} catch(err) {
console.log("code execution will never reach here");
}

1.2.2. 异步代码使用 try…catch

1
2
3
4
5
6
7
try {
setTimeout(function() {
noSuchVariable; // undefined variable
}, 1000);
} catch (err) {
console.log("code execution will never reach here");
}

未捕获的ReferenceError将在1秒后引发

➤ ⓧ Uncaught ReferenceError: noSuchVariable is not definedn

应该在异步代码以这种方式使用 try…catch 优雅地处理错误

1
2
3
4
5
6
7
setTimeout(function() {
try {
noSuchVariable;
} catch(err) {
console.log("error is caught here!");
}
}, 1000);

1.2.3. 嵌套 try…catch

我们还可以使用嵌套 trycatch 块,并向上抛出错误,如下所示:

1
2
3
4
5
6
7
8
9
10
try {
try {
throw new Error('Error while executing the inner code');
} catch (err) {
throw err;
}
} catch (err) {
console.log("Error caught by outer block:");
console.error(err.message);
}

Error caught by outer block:
➤ ⓧ Error while executing the code

1.3. try…finally

这是不推荐在使用 tryfinally(在之间的块不使用 catch)。让我们看看发生了什么:

1
2
3
4
5
try {
throw new Error('Error while executing the code');
} finally {
console.log('finally');
}

finally
➤ ⓧ Uncaught Error: Error while executing the code

我们应该在这里注意两件事:

  • 即使在 try 块抛出错误后,finally 块仍然执行
  • 如果没有 catch块,错误将无法被优雅地处理,从而导致未捕获错误

1.4. try…catch…finally

这是推荐使用 trycatch 块和可选finally 块。

1
2
3
4
5
6
7
8
9
10
try {
console.log("Start of try block");
throw new Error('Error while executing the code');
console.log("End of try block -- never reached");
} catch (err) {
console.error(err.message);
} finally {
console.log('Finally block always run');
}
console.log("Code execution outside try-catch-finally block continue..");

Start of try block
➤ ⓧ Error while executing the code
Finally block always run
Code execution outside try-catch-finally block continue…

我们还要在这里注意两件事:

  • try 块中抛出错误后的代码永远不会执行。
  • catch 块优雅地处理错误。
  • 即使在 try块 抛出错误后,finally 块仍然执行。

finally 块通常用于清理资源或关闭流,如下所示:

1
2
3
4
5
6
7
8
ry {
openFile(file);
readFile(file);
} catch (err) {
console.error(err.message);
} finally {
closeFile(file);
}

1.5. throw

throw 语句用于抛出异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// throw primitives and functions
throw "Error404";
throw 42;
throw true;
throw {toString: function() { return "I'm an object!"; } };

// throw error object
throw new Error('Error while executing the code');
throw new SyntaxError('Something is wrong with the syntax');
throw new ReferenceError('Oops..Wrong reference');

// throw custom error object
function ValidationError(message) {
this.message = message;
this.name = 'ValidationError';
}
throw new ValidationError('Value too high');

2. 异步代码中的错误处理

建议使用 Promisesasync await 异步代码(API调用),因为它们提供了对错误处理的支持。

2.1. then…catch with Promises

你可以使用 then()catch() 链接多个 promise来处理链中单个promise的错误,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Promise.resolve(1)
.then(res => {
console.log(res); // prints '1'

throw new Error('something went wrong'); // throw error

return Promise.resolve(2); // code will not reach here
})
.then(res => {
// code will not reach since promise not resolved in prev block here due to error
console.log(res);
})
.catch(err => {
console.error(err.message); // prints 'something went wrong'
return Promise.resolve(3);
})
.then(res => {
console.log(res); // prints '3'
})
.catch(err => {
// code will not reach since promise resolved in prev block
console.error(err);
})

让我们看一个更实际的例子,我们使用 fetch 调用API,它返回一个promise对象。我们使用catch块优雅地处理API失败。

1
2
3
4
5
6
7
8
9
10
11
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}

fetch("http://httpstat.us/500")
.then(handleErrors)
.then(response => console.log("ok"))
.catch(error => console.log("Caught", error));

Caught Error: Internal Server Error
at handleErrors (:3:15)

2.2. try…catch with async await

1
2
3
4
5
6
7
(async function() {
try {
await fetch("http://httpstat.us/500");
} catch (err) {
console.error(err.message);
}
})();

我们看一个同样的例子,我们使用fetch调用API返回一个promise对象。我们使用try..catch块优雅地处理API失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
}

(async function() {
try {
let response = await fetch("http://httpstat.us/500");
handleErrors(response);
let data = await response.json();
return data;
} catch (error) {
console.log("Caught", error)
}
})();

Caught Error: Internal Server Error
at handleErrors (:3:15)
at :11:7

3. 内置JavaScript错误

JavaScript有内置的错误对象,通常由try块和catch块抛出。

错误对象包含以下属性:

  • name: 是错误的名称,例如"error", “SyntaxError”, "ReferenceError"等。
  • Message: 是关于错误详细信息的消息
  • 堆栈: 用于调试目的的错误堆栈跟踪。
    让我们创建一个 Error Object 并查看它的namemessage属性:
1
2
3
4
5
const err = new Error('Error while executing the code');

console.log("name:", err.name);
console.log("message:", err.message);
console.log("stack:", err.stack);

name: Error
message: Error while executing the code
stack: Error: Error while executing the code
at :1:13

JavaScript 有以下继承自Error对象的内置错误:

3.2. EvalError

EvalError指示一个关于全局eval()函数的错误。此异常不再由JavaScript抛出,它的存在是为了向后兼容。

3.3. RangeError

当值超出范围时抛出 RangeError

➤ [].length = -1
ⓧ Uncaught RangeError: Invalid array length

3.4. ReferenceError

当引用一个不存在的变量时,会抛出ReferenceError

➤ x = x + 1;
ⓧ Uncaught ReferenceError: x is not defined

3.5. SyntaxError

当在 JavaScript 代码中使用任何错误的语法时,将抛出 SyntaxError

➤ function() { return ‘Hi!’ }
ⓧ Uncaught SyntaxError: Function statements require a function name
➤ 1 = 1
ⓧ Uncaught SyntaxError: Invalid left-hand side in assignment
➤ JSON.parse("{ x }");
ⓧ Uncaught SyntaxError: Unexpected token x in JSON at position 2

3.6. TypeError

当值不是期望的类型时,抛出 TypeError

➤ 1();
ⓧ Uncaught TypeError: 1 is not a function
null.name;
ⓧ Uncaught TypeError: Cannot read property ‘name’ of null

3.7. URIError

当全局 URI 处理函数以错误的方式使用时,抛出 URIError

➤ decodeURI("%%%");
ⓧ Uncaught URIError: URI malformed

4. 定义并抛出自定义错误

我们还可以这样定义自定义错误:

1
2
3
4
5
6
7
8
9
10
11
class CustomError extends Error {
constructor(message) {
super(message);
this.name = "CustomError";
}
};

const err = new CustomError('Custom error while executing the code');

console.log("name:", err.name);
console.log("message:", err.message);

name: CustomError
message: Custom error while executing the code

我们可以进一步增强 CustomError 对象,使其包含错误代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
class CustomError extends Error {
constructor(message, code) {
super(message);
this.name = "CustomError";
this.code = code;
}
};

const err = new CustomError('Custom error while executing the code', "ERROR_CODE");

console.log("name:", err.name);
console.log("message:", err.message);
console.log("code:", err.code);

name: CustomError
message: Custom error while executing the code
code: ERROR_CODE

我们在 try..catch 块中使用这个:

1
2
3
4
5
6
7
8
9
try{
try {
null.name;
}catch(err){
throw new CustomError(err.message, err.name); //message, code
}
}catch(err){
console.log(err.name, err.code, err.message);
}

CustomError TypeError Cannot read property ‘name’ of null

参考

关注【公众号】,了解更多。



支付宝打赏 微信打赏

赞赏一下 坚持原创技术分享,您的支持将鼓励我继续创作!