7、全部完成才结束Promise.all
场景
我们在写ajax的时候,经常会面临这样一个场景:
我有三个ajax请求,但是需要等着三个ajax的请求结果都出来后再进行处理。
如果常见写法,我们需要在每个都执行完之后,依次判断一下其他的完成了没有,完成了才能继续,没完成就return。
但是使用Promise.all
的话,问题简单多了。
Promise.all(iterable);
参数:
一般是一个数组,也可以是一个有迭代器接口的对象,但要求每个成员都是Promise实例。
返回值:
是一个Promise对象,为了方便称之为foo。
返回值的状态将维持pending不变,直到当参数的每个Promise实例的状态从pending变化为resolved,或者rejected时。
返回值Promise对象的变化规则:
1、参数的所有Promise都执行resolve,那么结果:
- foo的状态resolved;
- foo的then执行resolve;
- foo的值是一个数组;
- 参数里的Promise对象的值按顺序(迭代器里的顺序,而非状态变化顺序)被放入到这个数组中,作为foo的值使用;
2、假如参数里的Promise对象有一个执行了reject,那么结果:
- foo的状态rejected;
- foo的then执行reject;
- foo的值被执行了reject的那个Promise对象所决定;
- foo的reject将在那个执行了reject的Promise对象执行后立刻执行(而不是等所有的都执行完毕);
3、假如参数中的Promise对象有多个执行了reject,那么结果:
- 同上一种情况的1、2和4
- foo的值被第一个执行了reject的那个Promise对象所决定(后面的对其无影响);
情况一示例代码:
function delay(msg, time, isRej) {
return new Promise((res, rej) => {
setTimeout(() => {
isRej ? rej(msg) : res(msg)
}, time)
})
}
let promiseArray = [delay("first", 500), delay("second", 3000), delay("third", 1000)];
let foo = Promise.all(promiseArray);
foo.then(msg => {
console.log(msg)
}, err => {
console.log(err)
})
// ["first", "second", "third"] //3秒后
情况三示例代码(情况二可以参考情况三的,基本是一样的):
function delay(msg, time, isRej) {
return new Promise((res, rej) => {
setTimeout(() => {
isRej ? rej(msg) : res(msg)
}, time)
})
}
let promiseArray = [delay("first", 500), delay("second", 5500, true), delay("third", 1000, true)];
let foo = Promise.all(promiseArray);
foo.then(msg => {
console.log(msg)
}, err => {
console.log(err)
})
// third //1秒后
另外由于Promise.all的返回值也是一个Promise实例,因此适用于Promise实例的方法,自然也适用于它。
关于参数,更复杂的情况
假如Promise.all
的参数,不是一个单纯的new Promise()
对象,而是(new Promise()).then(() => {})
这样的,会发生什么事情呢?
首先,Promise.all执行哪个,不取决于第一个new Promise()里的状态,而是取决于.then()
执行之后的状态。
原因在于,(new Promise()).then(() => {})
这个表达式的值,是作为Promise.all()
的参数的。
而这个表达式的值,取决于.then()
的值,而这个值,和new Promise()
并不是同一个Promise对象(参照本博客前几章)。
因此假如new Promise()执行了reject,而then里的reject执行了resolve,那么最终结果还是resolved,而不是rejected。
8、谁快用谁的Promise.race
Promise.race(iterable);
参数:
同Promise.all,懒得解释了。
返回值:
也是一个Promise对象,为了方便称之为foo。
返回值变化规律:
简单来说,参数的所有Promise对象,哪个的状态最先变化,返回值foo就使用它的。
例如:
- 参数里有三个Promise对象;
- 他们状态变化所需要时间分别是2秒,1秒,3秒;
- 他们的状态变化结果分别是resolved,resolved,rejected;
- 他们的值分别为’A’, ‘B’,’C’;
- Promise.race执行,等1秒后,第二个Promise对象的状态变为resolved;
- 此时foo的状态立刻变为resolved,执行resolve,值为’B’;
- 另外两个的状态变化将不能影响foo;
如示例代码:
function delay(msg) {
let isFailed = Math.random() > 0.5;
let time = parseInt(Math.random() * 1000);
return new Promise((res, rej) => {
setTimeout(() => {
let data = {
msg,
time,
state: 'isFailed? ' + isFailed
}
isFailed ? rej(data) : res(data)
}, time)
})
}
let promiseArray = [delay("first"), delay("second"), delay("third")];
let foo = Promise.race(promiseArray);
foo.then(data => {
console.log(data.msg, data.time, data.state)
}, data => {
console.log(data.msg, data.time, data.state)
})
// 因为是随机结果,所以下面是随机结果之一
// second 207 isFailed? true
应用场景:
比如说你请求一个东西,但不想等待时间太久,比如说超过3秒钟就停止请求报告请求失败。
那么就可以使用这个,然后写一个Promise对象,3秒后执行reject的那种,作为最后一个参数。
然后只要超时,就自动执行,然后就ok了。
如示例:
function preventTimeout(promise) {
let timeout = new Promise((res, rej) => {
setTimeout(() => {
rej('超时了!')
}, 3000)
})
return Promise.race([promise, timeout])
}
let foo = new Promise((res, rej) => {
setTimeout(() => {
res("foo")
}, 4000);
})
let bar = preventTimeout(foo)
bar.then(null, val => {
console.log(val)
})
// 超时了! //3秒后
将需要进行超时检测的Promise对象,作为preventTimeout函数的参数,然后取用其返回值用于替代使用被检测的Promise对象。