先一句话描述下Promise.all()、Promise.race()和Promise.any()的区别。
一例胜千言,通过实例能更好地表示上面3种方法的区别。
无论是上传图片还是下载图片,都是异步操作,此时,我们可以将这两个常见交互变成了一个Promise操作,为了简化理解,整个异步过程我们就使用定时器代替,示意如下:
constupload=function(blob){lettime=Math.round(100+500*Math.random());returnnewPromise((resolve,reject)=>{//是否执行测试console.log(`run${time}ms`);//成功失败概率50%if(Math.random()>0.5){setTimeout(resolve,time,'promiseresolved'+time+'ms');}else{setTimeout(reject,time,'promiserejected'+time+'ms');}});};constload=function(url){//同upload};1.Promise.all()Promise.all()里面所有可迭代的Promise都通过则认为是成功,如果有一个拒绝,则认为失败。
测试如下:
(async()=>{try{letresult=awaitPromise.all([upload(0),upload(1),upload(2)]);console.log(result);}catch(err){console.error(err);}})();只有3个upload方法都resolve通过才会认为成功,否则返回第一个出错的提示结构,如下截图所示:
在本例中,Promise.all()成功的概率是0.5*0.5*0.5=0.125,也就是成功率是12.5%。
Promise.race()顾名思意就是“赛跑”,哪个执行快就使用哪个。
(async()=>{try{letresult=awaitPromise.race([load(0),load(1),load(2)]);console.log(result);}catch(err){console.error(err);}})();执行结果如下截图:
并且,成功的返回值不再是一个数组,而是单个提示。
在本例中,Promise.race()成功的概率是0.5,也就是成功率是50%。
Promise.any()更关心成功,只要有一个成功就可以了,除非所有的Promise都拒绝,否则就认为成功。
类似的测试代码:
(async()=>{try{letresult=awaitPromise.any([load(0),load(1),load(2)]);console.log(result);}catch(err){console.error(err);}})();执行结果如下截图:
可以看到出错的类型和上面两个方法都不一样,是AggregateError类型的错误。
AggregateError:Allpromiseswererejected
成功的返回值是第一个执行成功的Promise的返回值。
在本例中,Promise.any()成功的概率是(1–0.5*0.5*0.5)=87.5%,也就是成功率是87.5%。
这一小节讲下三种Promise方法合适的使用场景。
单纯举例,让大家更好地了解这3种Promise方法。
Promise.all()在图片批量上传的时候很有用,可以知道什么时候这批图片全部上传完毕,保证了并行,同时知道最终的上传结果。
上面的loading策略仔细一想,有些怪怪的,请求本来很快,还非要显示一个loading,这不是舍本逐末了吗?
所以需求应该是这样,如果请求可以在200ms内完成,则不显示loading,如果要超过200ms,则至少显示200ms的loading。
这个需求可以考虑使用Promise.race()方法,执行代码示意如下(getUserInfo、showUserInfo等方法都不变)。
执行结果如下截图示意:
可取消的Promise
找到一个Promise.race()方法的案例,出自MichaelClark,代码如下:
下面代码出自ChrisJensen,可以保持并行请求的数量固定。
代码如下:
Promise.any()适合用在通过不同路径请求同一个资源的需求上。
例如,Vue3.0在unpkg和jsdelivr都有在线的CDN资源,都是国外的CDN,国内直接调用不确定哪个站点会抽风,加载慢,这时候可以两个资源都请求,哪个请求先成功就使用哪一个。
我们就可以使用下面代码进行请求(使用动态import示意):
不过没关系,有了Promise.any(),就可以使用最快的那一个。
Promise.any()还有一个好处,那就是如果unpkg这个网站挂了,也不会影响Vue资源的加载,因为一个请求失败了,会继续请求其他的资源,也就是会去请求jsdelivr的资源。
这样保证了资源尽可能可用,但是尽可能使用加载最快的资源。
在这种场景下就很实用。
Promise.any()是后出来的规范,因此,兼容性相对滞后一些,如下图所示:
Safari14才支持,因此,目前生产环境还不能使用,毕竟我们厂子还有些产品需要兼容iOS9呢。