谨慎使用Promise.all
有一天, QA跟我抱怨,用户信息页面挂掉了, 整个页面的是空白的(无数据)。
打开浏览器调试了一下,发现只是一个获取用户爱好的接口挂掉了。那么一个接口挂掉了为什么会导致整个页面无数据呢?
先定位一下出问题的代码:
Promise.all([occupations(), hobbies(), vehicles(), user()])
.then(res => {
this.occupationItems = res[0]
this.hobbiesItems = res[1] // 这个数据拿不到
this.vehiclesItems = res[2]
this.user = res[3]
})
.catch(console.error)
这是用户信息页面的代码,用途是请求用户职业,喜好,名下车辆,用户详情等信息。请求回来之后,统一在then
里面进行处理。
回忆一下promise.all
的用法: 传递一个promise
的数组,当所有的promise
都完成(resolved
),回调所有成功的结果, 或者有一个失败, 回调第一个失败的结果
在当前状况下, hobbies
接口返回500
,promise
变成rejected
状态,然后就不会执行then
回调了。
为什么要用Promise.all
呢?
promise.all
将多个promise
放在一起处理,能简化回调的处理,一个then回调拿到所有数据进行处理,也能一个catch
回调捕获所有的异常。不然我们的代码得变成这样:
hobbies()
.then(hobbiesItems => { this.hobbiesItems = hobbiesItems })
.catch(console.error)
occupations()
.then(occupationItems => { this.occupationItems = occupationItems })
.catch(console.error)
vehicles()
.then(vehicleItems => { this.vehiclesItems = vehicleItems })
.catch(console.error)
user()
.then(currentUser => { this.user = currentUser })
.catch(console.error)
但是!!!
那么就意味着以下两点:
- 如果有一个回调执行失败,
then
是不会执行的,或者说,所有的promise
也都失败了 - 即使有几个
promise
已经进入resolved
状态,也会阻塞在那里直到所有的promise
完成
第一点会使得项目的容错大大降低,也许可能只是一个不关键的数据加载失败,其他所有的数据也不会显示。
第二点有违JavaScript
非阻塞的理念,如果一个回调迟迟不能拿到结果,大家就都干等着了。。
随手搜索了一下项目中的Promise.all
,心情复杂。。。
什么时候可以使用Promise.all
出于一个Promise
失败会影响所有Promise
回调执行以及promise.all会有一些阻塞的情况,我们应该谨慎使用。 那么我们什么情况可以使用Promise.all呢?我个人认为只有以下情况:
几个异步操作是强相关的,后续步骤必须依赖这几个步骤全部成功才能进行。
比如假设一个支付操作需要用户账户有余额,并且商品有库存,才能进行下一步操作,那么我们也许需要Promise.all
来处理。
Promise.all([getUserBalance(), getInventory()])
.then(() => {
// 做一些下单和支付的操作
})
.catch(error => alert(`can't pay: ${error.message}`))
所以, 你的项目中有滥用Promise.all
的情况吗?