Promise规范虽然不长,但细节也比较多,我挑出几个要点简单说明下:
- Promise 本质是一个状态机。每个 promise 只能是 3 种状态中的一种:pending、fulfilled 或 rejected。状态转变只能是 pending -> fulfilled 或者 pending -> rejected。状态转变不可逆。
- then 方法可以被同一个 promise 调用多次。
- then 方法必须返回一个 promise。规范里没有明确说明返回一个新的 promise 还是复用老的 promise(即 return this),大多数实现都是返回一个新的 promise,而且复用老的 promise 可能改变内部状态,这与规范也是相违背的。
Promise.all
let p1 = getjson(url, {}).then(function(res) {
return res;
});
let p2 = getjson(url, {
'entname': '',
'page': 1,
'pageSize': 10
}).then(function(res) {
return res;
});
Promise.all([p1, p2]).then((p1res) => {
console.log('p1::' + JSON.stringify(p1res[0].responseData));
console.log('p12::' + JSON.stringify(p1res[1].responseData));
});
使用promise返回的promise 在所有promise结束时可以进行调用
应用场景: 在今天,遇到了这样一个问题。在导入时,由于导入信息可能没有地址,也可能没有坐标,需要在导入时将坐标转换为地址或者将地址转换为坐标。但是,这个转换的过程是一个promise的过程(多且杂),目标:在所有转换结束后判断checkResultList是否为空数组,如果是给出通过检查。这里使用的即promise.all操作
另外还有Promise.race用来触发第一个promise结束的事件。
Promise的一些聪明用法
- 返回首个成功的Promise
从一组Promise里面得到第一个“成功的”结果,同时获得了并发执行的速度和容灾的能力。
Promise.race
不满足需求,因为如果有一个Promise率先reject,结果Promise也会立即reject;>Promise.all
也不满足需求,因为它会> 等待所有Promise,并且要求所有Promise都成功resolve。
function firstSuccess(promises){
return Promise.all(promises.map(p => {
// If a request fails, count that as a resolution so it will keep
// waiting for other possible successes. If a request succeeds,
// treat it as a rejection so Promise.all immediately bails out.
return p.then(
val => Promise.reject(val),
err => Promise.resolve(err)
);
})).then(
// If '.all' resolved, we've just got an array of errors.
errors => Promise.reject(errors),
// If '.all' rejected, we've got the result we wanted.
val => Promise.resolve(val)
);
}
把resolve的Promise反转成reject,把reject的Promise反转成resolve,然后用
Promise.all
合并起来。
这样的话,只要有一个原始Promise成功resolve,就会造成Promise.all
立刻被reject,实现提前退出!太巧妙了!
这个方法适合的场景:
- 有多条路可以走,其中任意一条路走通即可,其中有一些路失败也没关系
- 为了加速得到结果,并发地走多条路,避免瀑布式尝试
异步reduce
有时候业务逻辑要求我们必须串行地处理多个数据,不能像上面那样并发地处理多个数据。即,通过瀑布式的异步操作,将一个数组reduce成一个值。这个时候可以巧妙地使用array.reduce:
(async () => {
const data = [1, 2, 3]
const result = await data.reduce(async (accumP, current, index) => {
// 后面的处理要等待前面完成
const accum = await accumP;
const next = await apiCall(accum, current);
return next
}, 0);
console.log(result) // 6
async function apiCall(a, b) {
return new Promise((res)=> {
setTimeout(()=> {res(a+b);}, 300)
})
}
})()