2023-06-24 06:58:12 +08:00
|
|
|
|
## 异常处理方案
|
2023-06-11 17:19:39 +08:00
|
|
|
|
|
2023-06-17 23:56:31 +08:00
|
|
|
|
在JS开发中,**处理**异常包括两步:先**抛出**异常,然后**捕获**异常。
|
|
|
|
|
|
2023-06-24 06:58:12 +08:00
|
|
|
|
### 为什么要做异常处理
|
|
|
|
|
|
2023-06-17 23:56:31 +08:00
|
|
|
|
异常处理非常重要,至少有以下几个原因:
|
|
|
|
|
|
|
|
|
|
1. 防止程序报错甚至停止运行:当代码执行过程中发生错误或异常时,如果没有适当的异常处理机制,程序可能会报错、停止运行,甚至崩溃。通过处理异常,我们可以捕获错误并采取适当的措施避免系统报错。
|
|
|
|
|
2. 错误排查和调试:异常处理有助于定位和排查错误。可以通过捕获异常并输出相关信息,比如打印日志、错误上报、跟踪堆栈等等,以便快速定位问题所在,并进行调试和修复。
|
|
|
|
|
3. 提高代码健壮性和可靠性:可以采取适当的措施处理潜在的异常情况,从而减少程序出错的可能性。
|
|
|
|
|
4. 提升用户体验:通过兜底、容错、容灾等异常处理方案,可以向用户提供有效的错误信息提示,而不是让用户界面无响应甚至白屏。
|
2023-06-11 17:19:39 +08:00
|
|
|
|
|
2023-06-24 06:58:12 +08:00
|
|
|
|
### 抛出异常
|
2023-06-11 17:19:39 +08:00
|
|
|
|
|
2023-06-17 23:56:31 +08:00
|
|
|
|
抛出异常的使用场景举例:
|
2023-06-11 17:19:39 +08:00
|
|
|
|
|
2023-06-17 23:56:31 +08:00
|
|
|
|
我们经常会封装一些工具函数,这些函数可能给自己用,也可能给外部团队用。
|
|
|
|
|
|
|
|
|
|
在函数内部,如果不符合预期的业务逻辑,或者遇到异常情况时,很多人的写法是直接 return,不往下执行了。但是 return 的写法存在一个很大的弊端:**调用者不知道是因为函数内部没有正常执行,还是执行的返回结果就是一个undefined**。return 的写法只是规避了问题,没有解决问题。建议的做法是:我们**需要手动抛出异常**。
|
2023-06-11 17:19:39 +08:00
|
|
|
|
|
2023-06-24 06:58:12 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 捕获异常
|
|
|
|
|
|
|
|
|
|
如果只是抛出异常,而不捕获异常的话,是比较危险的。这意味着当前任务立即终止,不再执行(当然,后续的其他任务会正常执行)。此外,这个异常信息会层层往上,抛给上层的**调用者**。如果一直未被捕获,则最终会抛给**浏览器**,浏览器控制台就会报错。
|
|
|
|
|
|
|
|
|
|
接下来,我们看一下不同代码场景下的异常处理方案。
|
|
|
|
|
|
2023-06-24 17:15:09 +08:00
|
|
|
|
### 上报异常
|
|
|
|
|
|
|
|
|
|
如果有必要的话,你可以把异常信息和日志,上报给监控服务器,然后集中分析。我每天上班第一件事,就是打开监控系统,看错误日志,然后对症下药解决问题。
|
|
|
|
|
|
2023-06-24 06:58:12 +08:00
|
|
|
|
## 同步代码的异常处理
|
|
|
|
|
|
2023-06-11 17:19:39 +08:00
|
|
|
|
### 通过 throw 抛出异常
|
|
|
|
|
|
2023-06-17 23:56:31 +08:00
|
|
|
|
我们可以通过 `throw`关键字,抛出一个**用户自定义**的异常。当代码执行时遇到 throw 语句时,当前函数会停止停止,即:**当前函数** throw 后面的代码不会再执行。
|
|
|
|
|
|
2023-06-24 06:58:12 +08:00
|
|
|
|
throw 意思是,告诉调用者,当前被调用的函数报错了,调用者接下来需要捕获异常或者修改代码逻辑。
|
2023-06-17 23:56:31 +08:00
|
|
|
|
|
2023-06-11 17:19:39 +08:00
|
|
|
|
可以在 throw 的后面添加表达式或者数据类型,将添加的内容抛出去。数据类型可以是:number、string、boolean、对象等。
|
|
|
|
|
|
|
|
|
|
代码举例:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
function sum(num1, num2) {
|
|
|
|
|
if (typeof num1 !== "number") {
|
|
|
|
|
throw "type error: num1传入的类型有问题, 必须是number类型"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof num2 !== "number") {
|
|
|
|
|
throw "type error: num2传入的类型有问题, 必须是number类型"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return num1 + num2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sum('a', 'b');
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
打印结果:
|
|
|
|
|
|
|
|
|
|
![image-20230608180755608](https://img.smyhvae.com/image-20230608180755608.png)
|
|
|
|
|
|
|
|
|
|
当然,我们还可以 throw一个封装好的对象。比如:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
class myError {
|
|
|
|
|
constructor(errCode, errMsg) {
|
|
|
|
|
this.errCode = errMsg;
|
|
|
|
|
this.errMsg = errMsg;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function foo() {
|
|
|
|
|
throw new myError(-1, 'not login');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foo();
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
上面这种写法比较麻烦,一般不这么写。其实,JS中已经内置了 Error 类,专门用于生成错误信息。
|
|
|
|
|
|
|
|
|
|
### Error 类
|
|
|
|
|
|
|
|
|
|
JS内置的 Error 类非常好用。
|
|
|
|
|
|
|
|
|
|
代码举例:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
function foo() {
|
|
|
|
|
throw new Error('not login');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foo();
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
打印结果:
|
|
|
|
|
|
|
|
|
|
![image-20230608180103263](https://img.smyhvae.com/image-20230608180103263.png)
|
|
|
|
|
|
|
|
|
|
上面的打印结果可以看到,通过 Error 抛出来的错误,不仅可以看到报错信息,还可以看到调用栈,便于快速定位问题所在。非常方便。
|
|
|
|
|
|
2023-06-17 23:56:31 +08:00
|
|
|
|
|
2023-06-11 17:19:39 +08:00
|
|
|
|
|
2023-06-24 06:58:12 +08:00
|
|
|
|
### 通过 try catch 捕获异常
|
2023-06-11 17:19:39 +08:00
|
|
|
|
|
2023-06-24 06:58:12 +08:00
|
|
|
|
同步代码,只抛出异常,不捕获异常的代码举例:
|
2023-06-11 17:19:39 +08:00
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
function foo() {
|
|
|
|
|
throw new Error('not login');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foo();
|
2023-06-17 23:56:31 +08:00
|
|
|
|
// 当前任务立即终止,不再执行;下面这行代码和 foo() 都在同一个 同步任务 中
|
2023-06-11 17:19:39 +08:00
|
|
|
|
console.log('qianguyihao');
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
打印结果:
|
|
|
|
|
|
|
|
|
|
![image-20230608182003407](https://img.smyhvae.com/image-20230608182003407.png)
|
|
|
|
|
|
2023-06-17 23:56:31 +08:00
|
|
|
|
可以看到,最后一行的 log 并没有执行。
|
|
|
|
|
|
2023-06-24 06:58:12 +08:00
|
|
|
|
我们可以使用 try catch 抛出异常, 对上述代码进行改进。代码举例:
|
2023-06-11 17:19:39 +08:00
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
function foo() {
|
|
|
|
|
throw new Error('not login');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 通过 try catch 手动捕获异常
|
|
|
|
|
try {
|
|
|
|
|
foo();
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.log(err);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-17 23:56:31 +08:00
|
|
|
|
// 当前任务的后续代码会继续执行
|
2023-06-11 17:19:39 +08:00
|
|
|
|
console.log('qianguyihao');
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
打印结果:
|
|
|
|
|
|
|
|
|
|
![image-20230608182140002](https://img.smyhvae.com/image-20230608182140002.png)
|
|
|
|
|
|
|
|
|
|
### 通过 try catch finally 捕获异常
|
|
|
|
|
|
|
|
|
|
如果有些代码必须要执行,我们可以放到 finally 里。
|
|
|
|
|
|
|
|
|
|
- 不管是否遇到异常,finally的代码一定会执行。
|
|
|
|
|
- 如果 try 和 finally 中都有返回值,那么会使用finally中的返回值。
|
|
|
|
|
|
|
|
|
|
代码举例:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
function foo() {
|
|
|
|
|
throw new Error('not login');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 通过 try catch 捕获异常
|
|
|
|
|
try {
|
|
|
|
|
foo();
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.log(err);
|
|
|
|
|
} finally {
|
|
|
|
|
console.log("finally")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 后续代码会继续执行
|
|
|
|
|
console.log('qianguyihao');
|
|
|
|
|
```
|
|
|
|
|
|
2023-06-24 06:58:12 +08:00
|
|
|
|
### try catch 只能捕获同步代码的异常
|
2023-06-11 17:19:39 +08:00
|
|
|
|
|
2023-06-12 22:52:35 +08:00
|
|
|
|
try catch只能捕获同步代码里的异常,而 Promise.reject() 是异步代码。
|
2023-06-11 17:19:39 +08:00
|
|
|
|
|
2023-06-12 22:52:35 +08:00
|
|
|
|
原因是:当异步函数抛出异常时,对于宏任务而言,执行函数时已经将该函数推入栈,此时并不在 try-catch 所在的栈,所以 try-catch 并不能捕获到错误。对于微任务而言(比如 promise)promise 的构造函数的异常只能被自带的 reject 也就是.catch 函数捕获到。
|
2023-06-11 17:19:39 +08:00
|
|
|
|
|
2023-06-12 22:52:35 +08:00
|
|
|
|
参考链接:
|
|
|
|
|
|
|
|
|
|
- [try-catch 能抛出 promise 的异常吗](https://blog.csdn.net/xiaoluodecai/article/details/107297404)
|
2023-06-11 17:19:39 +08:00
|
|
|
|
|
2023-06-24 17:15:09 +08:00
|
|
|
|
|
|
|
|
|
|
2023-06-24 18:39:09 +08:00
|
|
|
|
### 使用 window.onerror 监听未被捕获的代码异常
|
2023-06-24 17:15:09 +08:00
|
|
|
|
|
|
|
|
|
如果JS代码抛出了异常但没有进行捕获,我们可以使用 JS 自带的 `window.onerror` 事件监听到这些错误。
|
|
|
|
|
|
|
|
|
|
代码举例:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// 监听同步代码的异常
|
|
|
|
|
window.onerror = (event) => {
|
|
|
|
|
console.error('onerror 监听到未被捕获的异常:', event)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function foo1() {
|
|
|
|
|
throw new Error('not login');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function foo2() {
|
|
|
|
|
throw new Error('network error');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foo1();
|
|
|
|
|
foo2();
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
打印结果:
|
|
|
|
|
|
|
|
|
|
![image-20230624162123559](https://img.smyhvae.com/image-20230624162123559.png)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-06-24 06:58:12 +08:00
|
|
|
|
## Promise 的异常处理
|
|
|
|
|
|
2023-06-24 07:36:54 +08:00
|
|
|
|
### reject() 会自动抛出异常
|
2023-06-24 06:58:12 +08:00
|
|
|
|
|
|
|
|
|
在使用 Promise 时,当我们调用了 reject() 之后,系统会**自动抛出异常**,不需要我们手动抛出异常。这是 Promise的内部机制。但是我们需要手动捕获异常。
|
|
|
|
|
|
2023-06-24 07:36:54 +08:00
|
|
|
|
当 Promise 进入 rejected状态之后,会触发 catch()方法的执行,捕获异常。此时,成功完成了Promise异常处理的闭环。
|
|
|
|
|
|
2023-06-24 17:15:09 +08:00
|
|
|
|
### 在then() 中抛出异常(重要)
|
2023-06-24 07:36:54 +08:00
|
|
|
|
|
|
|
|
|
当then()方法传入的回调函数中,如果遇到异常或者手动抛出异常,那么,then()所返回的**新的 Promise 会进入rejected 状态**,进而触发新Promise 的 catch() 方法的执行,做异常捕获。
|
|
|
|
|
|
|
|
|
|
**场景1**:在then()方法传入的回调函数中,如果代码**在执行时遇到异常**,系统会**自动抛出异常**。此时我们需要在 catch() 里**手动捕获异常**,否则会报错。
|
|
|
|
|
|
|
|
|
|
自动抛出异常的代码举例:(由于没有捕获异常,所以会报错)
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const myPromise = new Promise((resolve, reject) => {
|
|
|
|
|
resolve('qianguyihao1 fulfilled');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
myPromise.then(res => {
|
|
|
|
|
console.log('res1:', res);
|
|
|
|
|
// 显然,person 并没有 forEach()方法。所以,代码在执行时,会遇到异常。
|
|
|
|
|
const person = { name: 'vae' };
|
|
|
|
|
person.forEach(item => {
|
|
|
|
|
console.log('item:', item);
|
|
|
|
|
})
|
|
|
|
|
// 这行代码不会执行,因为上面的代码报错了
|
|
|
|
|
console.log('qianguyihao2');
|
|
|
|
|
}).then(res => {
|
|
|
|
|
console.log('res2:', res);
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 定时器里的代码正常执行
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
console.log('qianguyihao3');
|
|
|
|
|
}, 100)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
运行结果:
|
|
|
|
|
|
|
|
|
|
![image-20230615090007932](https://img.smyhvae.com/image-20230615090007932.png)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
代码改进:(代码在执行时遇到异常,此时我们捕获异常,所以系统不会报错,这才是推荐的写法)
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const myPromise = new Promise((resolve, reject) => {
|
|
|
|
|
resolve('qianguyihao1 fulfilled');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
myPromise.then(res => {
|
|
|
|
|
console.log('res1:', res);
|
|
|
|
|
// 显然,person 并没有 forEach()方法。所以,代码在执行时,会遇到异常。
|
|
|
|
|
const person = { name: 'vae' };
|
|
|
|
|
person.forEach(item => {
|
|
|
|
|
console.log('item:', item);
|
|
|
|
|
})
|
|
|
|
|
// 这行代码不会执行,因为上面的代码报错了
|
|
|
|
|
console.log('qianguyihao2');
|
|
|
|
|
}).then(res => {
|
|
|
|
|
console.log('res2:', res);
|
|
|
|
|
}).catch(err => {
|
|
|
|
|
// 在 catch()方法传入的会调函数里,捕获异常
|
|
|
|
|
console.log('err2:', err);
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 定时器里的代码正常执行
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
console.log('qianguyihao3');
|
|
|
|
|
}, 100)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
打印结果:
|
|
|
|
|
|
|
|
|
|
![image-20230624072927944](https://img.smyhvae.com/image-20230624072927944.png)
|
|
|
|
|
|
|
|
|
|
**场景2**:在then()方法传入的回调函数中,如果我们手动抛出异常,此时我们需要在 catch() 里**手动捕获异常**,否则会报错。
|
|
|
|
|
|
|
|
|
|
代码举例:(手动抛出异常,未捕获,所以会报错)
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const myPromise = new Promise((resolve, reject) => {
|
|
|
|
|
resolve('qianguyihao fulfilled 1');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
myPromise.then(res => {
|
|
|
|
|
console.log('res1:', res);
|
|
|
|
|
// 手动抛出异常
|
|
|
|
|
throw new Error('qianguyihao rejected 2')
|
|
|
|
|
}).then(res => {
|
|
|
|
|
console.log('res2:', res);
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 定时器里的代码正常执行
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
console.log('qianguyihao3');
|
|
|
|
|
}, 100)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
打印结果:
|
|
|
|
|
|
|
|
|
|
![image-20230624073252797](https://img.smyhvae.com/image-20230624073252797.png)
|
|
|
|
|
|
|
|
|
|
代码改进:(代码在执行时遇到异常,此时我们捕获异常,所以系统不会报错,这才是推荐的写法)
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const myPromise = new Promise((resolve, reject) => {
|
|
|
|
|
resolve('qianguyihao fulfilled 1');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
myPromise.then(res => {
|
|
|
|
|
console.log('res1:', res);
|
|
|
|
|
// 手动抛出异常
|
|
|
|
|
throw new Error('qianguyihao rejected 2')
|
|
|
|
|
}).then(res => {
|
|
|
|
|
console.log('res2:', res);
|
|
|
|
|
}, (err) => {
|
|
|
|
|
console.log('err2:', err);
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 定时器里的代码正常执行
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
console.log('qianguyihao3');
|
|
|
|
|
}, 100)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
打印结果:
|
|
|
|
|
|
|
|
|
|
![image-20230624073604599](https://img.smyhvae.com/image-20230624073604599.png)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-06-24 17:15:09 +08:00
|
|
|
|
|
|
|
|
|
|
2023-06-24 18:39:09 +08:00
|
|
|
|
### 使用 unhandledrejection 事件监听未被捕获的Promise异常
|
2023-06-24 17:15:09 +08:00
|
|
|
|
|
|
|
|
|
如果Promise抛出了异常但没有进行捕获,我们可以使用JS自带的 `unhandledrejection` 事件监听到这些错误。这个事件非常有用,尤其是当我们需要**集中做日志收集**时,屡试不爽。这个事件只能用于监听 Promise 中的异常,不能用于其他同步代码的异常。
|
|
|
|
|
|
|
|
|
|
先来看下面这行代码:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const myPromise = new Promise((resolve, reject) => {
|
|
|
|
|
console.log('qianguyihao1');
|
|
|
|
|
reject('not login');
|
|
|
|
|
console.log('qianguyihao2');
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
打印结果:
|
|
|
|
|
|
|
|
|
|
![image-20230624154609747](https://img.smyhvae.com/image-20230624154609747.png)
|
|
|
|
|
|
|
|
|
|
上面的代码抛出了异常,但没有捕获异常,所以我们可以用 unhandledrejection 事件监听到。代码举例:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// 监听未被捕获的 Promise 异常
|
|
|
|
|
window.addEventListener('unhandledrejection', (event) => {
|
2023-06-24 18:39:09 +08:00
|
|
|
|
console.error(`unhandledrejection 监听到异常,写法1: ${event.reason}`)
|
2023-06-24 17:15:09 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
window.onunhandledrejection = event => {
|
2023-06-24 18:39:09 +08:00
|
|
|
|
console.error(`unhandledrejection 监听到异常,写法2: ${event.reason}`);
|
2023-06-24 17:15:09 +08:00
|
|
|
|
};
|
|
|
|
|
|
2023-06-24 18:39:09 +08:00
|
|
|
|
window.onerror = (event) => {
|
|
|
|
|
console.error('onerror 监听到异常:', event);
|
|
|
|
|
};
|
2023-06-24 17:15:09 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const promise1 = new Promise((resolve, reject) => {
|
|
|
|
|
reject('not login');
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const promise2 = new Promise((resolve, reject) => {
|
2023-06-24 18:39:09 +08:00
|
|
|
|
throw new Error('network error');
|
|
|
|
|
resolve();
|
2023-06-24 17:15:09 +08:00
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
打印结果:
|
|
|
|
|
|
2023-06-24 18:39:09 +08:00
|
|
|
|
![image-20230624172634569](https://img.smyhvae.com/image-20230624172634569.png)
|
2023-06-24 17:15:09 +08:00
|
|
|
|
|
|
|
|
|
可以看到,promise1 和 Promise2 的异常,都被 unhandledrejection 事件**收集**到了。
|
|
|
|
|
|
2023-06-24 18:39:09 +08:00
|
|
|
|
代码举例2:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
window.addEventListener('unhandledrejection', (event) => {
|
|
|
|
|
console.error(`unhandledrejection 监听到异常: ${event.reason}`)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
window.onerror = (event) => {
|
|
|
|
|
console.error('onerror 监听到异常:', event);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const myPromise = new Promise((resolve, reject) => {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
throw new Error('not login');
|
|
|
|
|
resolve();
|
|
|
|
|
}, 100);
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
打印结果:
|
|
|
|
|
|
|
|
|
|
![image-20230624172350994](https://img.smyhvae.com/image-20230624172350994.png)
|
|
|
|
|
|
|
|
|
|
上面的代码中,unhandledrejection 无法监听异常,因为定时器里的代码属于宏任务。
|
|
|
|
|
|
|
|
|
|
### resolve()之后,再报错无效
|
|
|
|
|
|
|
|
|
|
代码举例:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const myPromise = new Promise((resolve, reject) => {
|
|
|
|
|
resolve('fulfilled');
|
|
|
|
|
throw new Error("自定义错误");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
myPromise.then(res => {
|
|
|
|
|
console.log("res", res);
|
|
|
|
|
return res + 1;
|
|
|
|
|
}).catch(err => {
|
|
|
|
|
console.log("err:", err);
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
打印结果:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
res fulfilled
|
|
|
|
|
```
|
2023-06-24 17:15:09 +08:00
|
|
|
|
|
2023-06-24 18:39:09 +08:00
|
|
|
|
上方代码中,第3行的异常代码相当于没写。因为 resolve()之后,Promise的状态会立即进入 fulfilled,然后走到 then(),状态不可逆。
|
2023-06-24 17:15:09 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-06-24 07:36:54 +08:00
|
|
|
|
## async await 的异常处理
|
|
|
|
|
|
|
|
|
|
### 捕获异常
|
2023-06-24 06:58:12 +08:00
|
|
|
|
|
|
|
|
|
代码举例:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
function requestData1() {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
reject('任务1失败');
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function requestData2() {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
resolve('任务2成功');
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function getData() {
|
|
|
|
|
// requestData1 在执行时,遇到异常
|
|
|
|
|
await requestData1();
|
|
|
|
|
/*
|
|
|
|
|
由于上面的代码在执行是遇到异常,所以,这里虽然什么都没写,底层默认写了如下代码:
|
|
|
|
|
return Promise.reject('任务1失败');
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// 下面这两行代码不会再执行了,因为上面的代码遇到了异常
|
|
|
|
|
console.log('qianguyihao1');
|
|
|
|
|
await requestData2();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getData();
|
|
|
|
|
|
|
|
|
|
// 【注意】定时器里的代码会正常实行,因为它在另外一个宏任务里,不在上面的微任务里
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
console.log('qianguyihao2');
|
|
|
|
|
}, 100)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
打印结果:
|
|
|
|
|
|
|
|
|
|
![image-20230615085743284](https://img.smyhvae.com/image-20230615085743284.png)
|
|
|
|
|
|
|
|
|
|
所以,为了避免上述问题,我们还需要手动捕获异常。我们捕获到异常之后,这个异常就不会继续网上抛了,更不会抛给浏览器。
|
2023-06-17 23:56:31 +08:00
|
|
|
|
|
2023-06-24 17:15:09 +08:00
|
|
|
|
## 高级用法
|
|
|
|
|
|
|
|
|
|
###
|
|
|
|
|
|
2023-06-17 23:56:31 +08:00
|
|
|
|
## 赞赏作者
|
|
|
|
|
|
|
|
|
|
创作不易,你的赞赏和认可,是我更新的最大动力:
|
|
|
|
|
|
|
|
|
|
![](https://img.smyhvae.com/20220401_1800.jpg)
|
|
|
|
|
|