mirror of https://github.com/qianguyihao/Web.git
add(Promise): then() 中传入非函数时,会发生值穿透
This commit is contained in:
parent
a74ed1a3f5
commit
2444bf14ac
|
|
@ -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的状态**。
|
||||
|
||||
还有一种特殊情况:
|
||||
|
|
|
|||
|
|
@ -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 ,获取整体的成功/失败结果。
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
})
|
||||
```
|
||||
|
||||
打印结果:
|
||||
|
||||

|
||||

|
||||
|
||||
可以看到,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);
|
||||
})
|
||||
```
|
||||
|
||||
打印结果:
|
||||
|
||||

|
||||
|
||||
上面的代码中,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(),状态不可逆。
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue