Async/await 的危险
在为一个大型应用程序的重写提供几个月的咨询之后,我意识到async
/await
实际上已经用于大多数异步操作,而并行执行似乎并不适用。例如,请考虑以下 Vue 代码片段:
async initStore(query) {
await this.getConfig();
await this.getUser();
await this.checkRussianContext(query);
await this.getBasket(this.$store.state.config.selectedCurrency),
await this.$store.dispatch('options/fetchOptions', {
basket : this.$store.state.basket,
});
},
这里,每一行代码都会在其前一行完成时执行。这意味着getUser
它会等待getConfig
数据获取完成后再执行。
看到这段代码时,我想到以下几点:
- 如果一行不需要前一行的数据怎么办?为什么要阻止它的执行并降低应用程序的速度?
- 我们可以使用类似的东西并行运行不相关的方法吗
Promise.all
? - 相关方法可能应该使用
then
块来避免阻塞其余方法
本文的目的是通过向您展示在某些情况下默认使用/会对性能和用户体验产生巨大影响来帮助您发现这种代码异味。async
await
不相关的查询应该并行执行
让我们看一些具体的数据,好吗?
以下是我们将要分析的代码片段:
const getUserData = async () => {
// Get a random dog as our user's avatar
const res = await fetch('https://dog.ceo/api/breeds/image/random')
const { message } = await res.json()
// Get our user's general data
const user = await fetch('https://randomuser.me/api/')
const { results } = await user.json()
// ...
}
在快速 3G 上运行此代码片段 100 次(使用 Chrome 的开发工具),平均执行时间为1231.10ms。
但是,既然第二个查询不需要第一个查询的结果,为什么要阻止它呢?让我们将代码更改为以下内容,并重新运行 100 次。
const getUserDataFaster = async () => {
// Execute both requests in parallel
const [res, user] = await Promise.all([
fetch('https://dog.ceo/api/breeds/image/random'),
fetch('https://randomuser.me/api/')
])
const [{ message }, { results }] = await Promise.all([res.json(), user.json()])
// ...
}
现在,平均执行时间为612.50ms,是两个查询依次执行所需时间的一半。
重点是:如果您可以并行执行耗时的查询,那就这样做。
在这个codepen上亲自尝试一下。
不相关的代码不应该等待
让我们以我的第一个例子为例,但稍加改动:
async initStore(query) {
await Promise.all([
this.getConfig(),
this.getUser(),
this.checkRussianContext(query)
])
await this.getBasket(this.$store.state.config.selectedCurrency),
await this.$store.dispatch('options/fetchOptions', {
basket : this.$store.state.basket,
});
await initBooking()
},
这里,前三个请求是并行执行的,而接下来的请求依赖于事先获取的数据,因此会在之后执行。虽然这段代码存在问题,但你发现了吗?
可怜的小家伙initBooking
必须等待getBasket
和都fetchOptions
完成后才能执行,即使这与它们要获取的数据无关。一个简单的解决方案是用一个简单的区块
来交易。await
then
async initStore(query) {
await Promise.all([
this.getConfig(),
this.getUser(),
this.checkRussianContext(query)
])
this.getBasket(this.$store.state.config.selectedCurrency).then(async () => {
await this.$store.dispatch('options/fetchOptions', {
basket : this.$store.state.basket,
});
})
await initBooking()
},
这样,和getBasket
将initBooking
同时执行。
想亲眼看看吗?查看这个codepen,它演示了我的示例。
我将就此结束本文,以免向您提供过多的例子,但您现在应该已经了解其要点了。
async
/await
是 Javascript 语言的精彩补充,但我希望您现在问自己,它们是否适合您正在使用的特定方法,更重要的是:您的某些查询是否可以并行执行。
感谢您的阅读,如果您在 Twitter 上关注我@christo_kade我会很高兴的,这样我们就可以分享我们对awaits
❤️ 的相互怀疑