什么是异常
Error objects are thrown when runtime errors occur. The Error object can also be used as a base object for user-defined exceptions.
描述的很简单,我们总结一下就是代码在执行过程中遇到了问题,程序已经无法正常运行了,Error对象会被抛出,这一点它不同于大部分编程语言里使用的异常对象Exception,甚至更适合称之为错误,应该说事实也确实如此,Error对象在未被抛出时候和js里其他的普通对象没有任何差别是不会引发异常的,同时Error 对象也可用于用户自定义错误的基础对象。
try {
const 123variable = 2;
} catch(e) {
console.log('捕获到了:', e)
}
<script>
function throwSomeError() {
throw new Error('抛个异常玩玩');
console.log('我估计是凉了,不会执行我了!');
}
throwSomeError();
console.log('那么我呢?')
</script>
<script>
console.log('大家猜猜我会执行吗?');
</script>
异常的类型
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取分页数据
const getPagedData = (pageIndex, pageSize) => {
if(pageIndex < 0 || pageSize < 0 || pageSize > 1000) {
throw new RangeError(`pageIndex 必须大于0, pageSize必须在0和1000之间`);
}
return [];
}
// 转换时间格式
const dateFormat = (dateObj) => {
if(dateObj instanceof Date) {
return 'formated date string';
}
throw new TypeError('传入的日期类型错误');
}
new Error('出错了!');
console.log('我吃嘛嘛香,喝嘛嘛棒!'); // 正常输出 '我吃嘛嘛香,喝嘛嘛棒!'
// bad
throw '出错了';
throw 123;
throw [];
throw null;
异常捕获
try {
// 要运行的代码,可能引发异常
doSomethingMightThrowError();
}
catch (error) {
// 处理异常的代码块,当发生异常时将会被捕获,如果不继续throw则不会再向上传播
// error为捕获的异常对象
// 这里一般能让程序恢复的代码
doRecovery();
}
finally {
// 无论是否出现异常,始终都会执行的代码
doFinally();
}
// Timeout
try {
setTimeout(() => {
throw Error("定时器出错了!");
}, 1000);
} catch (error) {
console.error(error.message);
}
// Events
try {
window.addEventListener("click", function() {
throw Error("点击事件出错了!");
});
} catch (error) {
console.error(error.message);
}
const promiseA = new Promise((resolve,reject)=>{
throw new Error('Promise出错了!');
});
const doSomethingWhenResolve = () => {};
const doSomethingWhenReject = (error) => {
logger.log(error)
}
// 使用catch捕获
const promiseB = promiseA.then(doSomethingWhenResolve).catch(doSomethingWhenReject);
// 等价于
const promiseB = promise.then(doSomethingWhenResolve, doSomethingWhenResolve);
promiseB.then(() => {
console.log('我又可以正常进到then方法了!');
}).catch(()=>{
console.log('不会来这里!');
})
Prefer Exceptions to Returning Error Codes
1、业务流程更加清晰易读,我们把异常和业务流程理解为两个不同的问题,可以分开去处理;
2、分开来的两个逻辑都更加聚焦,代码更简洁;
// Dirty
class Laptop {
sendShutDown() {
const deviceID = getID(DEVICE_LAPTOP);
if (deviceID !== DEVICE_STATUS.INVALID) {
pauseDevice(deviceID);
clearDeviceWorkQueue(deviceID);
closeDevice(deviceID);
} else {
logger.log('Invalid handle for: ' + DEVICE_LAPTOP.toString());
}
}
getID(status) {
...
// 总是会返回deviceID,无论是不是合法有效的
return deviceID;
}
}
// Clean
class Laptop {
sendShutDown() {
try {
tryToShutDown();
} catch (error) {
logger.log(error);
}
}
tryToShutDown() {
const deviceID = getID(DEVICE_LAPTOP);
pauseDevice(deviceID);
clearDeviceWorkQueue(deviceID);
closeDevice(deviceID);
}
getID(status) {
...
throw new DeviceShutDownError('Invalid handle for: ' + deviceID.toString());
...
return deviceID;
}
}
Don't ignore caught error!
// bad
try {
doSomethingMightThrowError();
} catch (error) {
console.log(error);
}
// good
try {
doSomethingMightThrowError();
} catch (error){
console.error(error);
message.error(error.message);
logger.log(error);
}
Don't ignore rejected promises!
// bad
fetchData().then(doSomethingMightThrowError).catch(console.log);
// good
fetchData()
.then(doSomethingMightThrowError)
.catch(error => {
console.error(error);
message.error(error.message);
logger.log(error);
});
Exceptions Hierarchy
export class RequestException extends Error {
constructor(message) {
super(`RequestException: ${mesage}`);
}
}
export class AccountException extends Error {
constructor(message) {
super(`AccountException: ${message}`);
}
}
const AccountController = {
getAccount: (id) => {
...
throw new RequestException('请求账户信息失败!');
...
}
}
// 客户端代码,创建账户
const id = 1;
const account = AccountController.getAccount(id);
if(account){
throw new AccountException('账户已存在!');
}
Provide context with exceptions
错误边界是一种 React 组件,这种组件可以捕获发生在其子组件树任何位置的 JavaScript 错误,并打印这些错误,同时展示降级 UI,而并不会渲染那些发生崩溃的子组件树。错误边界可以捕获发生在整个子组件树的渲染期间、生命周期方法以及构造函数中的错误。
import { Result } from 'antd';
import type { ErrorInfo } from 'react';
import React from 'react';
// eslint-disable-next-line @typescript-eslint/ban-types
class ErrorBoundary extends React.Component<
{ children?: React.ReactNode },
{ hasError: boolean; errorInfo: string }
> {
state = { hasError: false, errorInfo: '' };
static getDerivedStateFromError(error: Error) {
return { hasError: true, errorInfo: error.message };
}
componentDidCatch(error: any, errorInfo: ErrorInfo) {
// You can also log the error to an error reporting service
// eslint-disable-next-line no-console
console.log(error, errorInfo);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <Result status="error" title="Something went wrong." extra={this.state.errorInfo} />;
}
return this.props.children;
}
}
export { ErrorBoundary };
window.onerror事件
// message:错误信息(字符串)。
// source:发生错误的脚本URL(字符串)
// lineno:发生错误的行号(数字)
// colno:发生错误的列号(数字)
// error:Error对象(对象)
window.onerror = function(message, source, lineno, colno, error) {
logger.log('捕获到异常:',{ message, source, lineno, colno, error });
}
unhandledrejection事件
window.addEventListener('unhandledrejection', (e) => {
console.error('catch', e)
}, true)
1、将面向开发的异常信息转换成更友好的用户界面提示;
参考链接:
[1]https://developer.mozilla.org/zh-CN/Core_JavaScript_1.5_Reference/Global_Functions/eval
[2]https://reactjs.org/docs/error-boundaries.html
往期推荐
《Java工程师必读手册》
工匠追求“术”到极致,其实就是在寻“道”,且离悟“道”也就不远了,亦或是已经得道,这就是“工匠精神”——一种追求“以术得道”的精神。 如果一个工匠只满足于“术”,不能追求“术”到极致去悟“道”,那只是一个靠“术”养家糊口的工匠而已。作者根据多年来的实践探索,总结了大量的Java编码之“术”,试图阐述出心中的Java编码之“道”。
点击阅读原文查看详情。