add(Promise): then() 中传入非函数时,会发生值穿透

This commit is contained in:
qianguyihao 2023-06-24 18:39:09 +08:00
parent a74ed1a3f5
commit 2444bf14ac
3 changed files with 112 additions and 25 deletions

View File

@ -141,13 +141,13 @@ myPromise.then().then().catch()
2、当 then()方法中的回调函数中,手动 return 一个返回值时,那么 Promise 的状态取决于返回值的类型。当返回值这行代码执行完毕后, Promise 会立即决议,进入确定状态(成功 or 失败)。具体情况如下:
- 情况1如果没有返回值相当于 return undefined或者返回值是**普通值/普通对象**,那么 Promise 的状态为fulfilled。这个值会作为then()回调的参数
- 情况2如果返回值是**另外一个新的 Promise**,那么原 Promise 的状态将**交给新的 Promise 决定**。
- 情况1如果没有返回值相当于 return undefined或者返回值是**普通值/普通对象**,那么 Promise 的状态为fulfilled。这个值会作为fulfilled 状态的回调函数的参数值
- 情况2如果返回值是**另外一个新的 Promise**,那么原 Promise 的状态将**交给新的 Promise 决定**这两个Promise 的状态一致
- 情况3如果返回值是一个对象并且这个对象里有实现then()方法(这种对象称为 **thenable** 对象那就会执行该then()方法,并且根据**then()方法的结果来决定Promise的状态**。
还有一种特殊情况:
- 情况4当then()方法传入的回调函数遇到异常或者手动抛出异常时,那么, Promise 处于rejected 状态。
- 情况4当then()方法传入的回调函数遇到异常或者手动抛出异常时,那么, Promise 处于rejected 状态,并将抛出的错误作为 rejected 状态的回调函数的参数值
**小结**then()方法里,我们可以通过 return **传递结果和状态**给下一个新的Promise。
@ -246,7 +246,7 @@ res3: undefined
### 返回新的 Promise
情况1、在 then() 方法的回调函数中 return 一个成功的新 Promise相当于把新Promise的成功结果传递出去。代码举例
情况1、在 then() 方法的回调函数中 return 一个成功的新 Promise那么then()返回的Promise 也是成功状态。相当于把新Promise的成功结果传递出去。代码举例
```js
const promise1 = new Promise((resolve, reject) => {
@ -279,7 +279,7 @@ res2: qianguyihao fulfilled 2
res3 undefined
```
情况2、在 then() 方法的回调函数中 return 一个失败的新 Promise再继续往下走会怎么样相当于把新Promise 的失败原因传递出去。代码举例:
情况2、在 then() 方法的回调函数中 return 一个失败的新 Promise那么then()返回的Promise 也是失败状态。再继续往下走会怎么样相当于把新Promise 的失败原因传递出去。代码举例:
```js
const promise1 = new Promise((resolve, reject) => {
@ -320,6 +320,8 @@ res3: undefined
上方代码可以看到第二个Promise走的是失败回调这很容易理解。重点是最后一个 Promise 走的是成功回调,这很出人意料。我们稍后学习 catch()方法的返回值后,就能看懂。**这例子很经典,一定要记住**。
情况3在 then() 方法的回调函数中 return 一个 pending 状态的新 Promise那么 then() 返回的Promise状态也是 pending。
### 返回 thenable 对象
代码举例:
@ -360,6 +362,37 @@ res3 undefined
这方面的内容,我们在后续的文章《异常处理方案》中会详细讲解。
### 特殊情况then() 中传入非函数时,会发生值穿透
在Promise的`then()`方法中如果传入一个非函数作为参数JS 会将其忽略,并且将前一个 Promise 的结果值传递给下一个`then()`方法。这意味着如果你在`then()`中传入非函数参数它将被视为一个空操作而不会对Promise链产生任何影响。
“值穿透”的意思是,传入的非函数值会被忽略。
代码举例:
```js
const myPromise = new Promise((resolve, reject) => {
resolve('Hello');
});
myPromise
.then('Invalid Argument')
.then(res1 => {
console.log('res1:', res1);
return 'World';
})
.then(res2 => {
console.log('res2:', res2);
});
```
打印结果:
```
res1: Hello
res2: World
```
## Promise 实例的 catch() 方法
@ -468,7 +501,7 @@ myPromise.then().then().catch().then()
2、当 catch方法中的回调函数中手动 return 一个返回值时,那么 Promise 的状态取决于返回值的类型。当返回值这行代码执行完毕后, Promise 会立即决议,进入确定状态(成功 or 失败进而触发下一个then/catch 函数的执行。同时可以给下一个 then/catch 传递参数。具体情况如下:
- 情况1如果没有返回值相当于 return undefined或者返回值是**普通值/普通对象**,那么 Promise 的状态为fulfilled。这个值会作为then()回调的参数。
- 情况2如果返回值是**另外一个新的 Promise**,那么原 Promise 的状态将**交给新的 Promise 决定**。
- 情况2如果返回值是**另外一个新的 Promise**,那么原 Promise 的状态将**交给新的 Promise 决定**。这两个Promise 的状态一致。
- 情况3如果返回值是一个对象并且这个对象里有实现then()方法(这种对象称为 **thenable** 对象那就会执行该then()方法,并且根据**then()方法的结果来决定Promise的状态**。
还有一种特殊情况:

View File

@ -279,6 +279,8 @@ Promise.all([promise1, promise2, promise3])
案例:现在有一个**图片上传**的接口,每次请求接口时只能上传一张图片。需求是:当用户连续上传完九张图片(正好凑齐九宫格)之后,给用户一个“上传成功”的提示。这个时候,我们就可以使用`Promsie.all()`。
这个例子,在实际的项目开发中,经常遇到,属于高频需求,需要记住并理解。
1、代码举例如下
```js
@ -311,26 +313,30 @@ Promise.all(promiseArr)
1只有九张图片都上传成功才会走到 then。
第一张图会成功调 upload 接口,并返回 imgUrl但不会走到 resolve因为要等其他八张图的执行结果再决定是一起走 resolove 还是一起走 reject。
2按时间顺序来看假设第一张图片上传成功第二张图片上传失败那么最终的表现是
- 对于前端来说,九张图都会走到 reject整体会走到 catch不会走到 then。
- 对于后端来说,第一张图片会上传成功(因为写入 DB 是不可逆的),第二张图上传失败,剩下的七张图,会正常请求 upload img 接口。
3、**特别说明**
**其实九张图的 upload img 请求都已经发出去了**。对于后端来说,是没有区别的(而且读写 DB 的操作不可逆),只是在前端的交互表现不同、走到 resolve / reject / then / catch 的时机不同而已。
- 第一张图会成功调 upload 接口,并返回 imgUrl但不会走到 resolve因为要等其他八张图的执行结果再决定是一起走 resolove 还是一起走 reject。
- 当执行 Promise.all() / Promise.race() / Promise.any() 的时候,**其实九张图的 upload img 请求都已经发出去了**。对于后端来说,是没有区别的(而且读写 DB 的操作不可逆),只是在前端的交互表现不同、走到 resolve / reject / then / catch 的时机不同而已。
上面这个例子,在实际的项目开发中,经常遇到,属于高频需求,需要记住并理解。
4、**思维拓展**
3、**思维拓展**
- 拓展 1如果你希望九张图同时上传并且想知道哪些图上传成功、哪些图上传失败则可以这样做**无论 upload img 接口请求成功与否,全都执行 resolve**。这样的话,最终一定会走到 then然后再根据接口返回的结果判断九张图片的上传成功与否。
- 拓展 2实战开发中在做多张图片上传时可能是一张一张地单独上传各自的上传操作相互独立。此时 `Promise.all`便不再适用,这就得具体需求具体分析了。
### 注意:某个任务失败之后,其他任务会继续执行
一定要注意,当执行 Promise.all() / Promise.race() / Promise.any() 等方法时,如果其中一个任务失败了,**其他任务并没有停止,会继续执行**。只是前端拿不到其他任务的执行状态而已。
其他任务是否需要做一些特殊梳理,就要结合你自己的业务逻辑来考虑。
## Promse.allSettled()
Promise.all()方法组成的多个Promise中有个明显的特点是只要有一个 Promise 元素进入 rejected 状态,则整体的 Promise 会立即进入 rejected 状态。其他 Promise 元素会处于 pending 状态,任务本身是否执行成功,我们在前端代码里无从知晓,因为无法拿到处理结果。我们只知道整体的 Promise 是 fulfilled或者 rejected ,获取整体的成功/失败结果。

View File

@ -186,7 +186,7 @@ try catch只能捕获同步代码里的异常而 Promise.reject() 是异步
### 使用 window.onerror 收集未被捕获的代码异常
### 使用 window.onerror 监听未被捕获的代码异常
如果JS代码抛出了异常但没有进行捕获我们可以使用 JS 自带的 `window.onerror` 事件监听到这些错误。
@ -355,7 +355,7 @@ setTimeout(() => {
### 使用 unhandledrejection 事件收集未被捕获的Promise异常
### 使用 unhandledrejection 事件监听未被捕获的Promise异常
如果Promise抛出了异常但没有进行捕获我们可以使用JS自带的 `unhandledrejection` 事件监听到这些错误。这个事件非常有用,尤其是当我们需要**集中做日志收集**时,屡试不爽。这个事件只能用于监听 Promise 中的异常,不能用于其他同步代码的异常。
@ -378,36 +378,84 @@ const myPromise = new Promise((resolve, reject) => {
```js
// 监听未被捕获的 Promise 异常
window.addEventListener('unhandledrejection', (event) => {
console.error(`监听到异常写法1: ${event.reason}`)
console.error(`unhandledrejection 监听到异常写法1: ${event.reason}`)
});
window.onunhandledrejection = event => {
console.error(`监听到异常写法2: ${event.reason}`);
console.error(`unhandledrejection 监听到异常写法2: ${event.reason}`);
};
// window.onerror 无法监听 Promise 中的异常
// window.onerror= (event) => {
// console.error(`onerror 捕获到错误: ${event.reason}`)
// };
window.onerror = (event) => {
console.error('onerror 监听到异常:', event);
};
const promise1 = new Promise((resolve, reject) => {
reject('not login');
// 上面这一行,如果写成 throw new Error('not login') 的话,效果等价
})
const promise2 = new Promise((resolve, reject) => {
reject('network error');
throw new Error('network error');
resolve();
})
```
打印结果:
![image-20230624162359866](https://img.smyhvae.com/image-20230624162359866.png)
![image-20230624172634569](https://img.smyhvae.com/image-20230624172634569.png)
可以看到promise1 和 Promise2 的异常,都被 unhandledrejection 事件**收集**到了。
代码举例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
```
上方代码中第3行的异常代码相当于没写。因为 resolve()之后Promise的状态会立即进入 fulfilled然后走到 then(),状态不可逆。