当前位置: 首页 > article >正文

提升前端性能:如何优化多个异步请求的执行效率Promise.all()

提升前端性能:如何优化多个异步请求的执行效率Promise.all()

在现代的前端开发中,异步请求几乎是不可避免的。无论是从服务器获取数据,还是与外部API进行交互,都需要依赖异步操作来提升用户体验。

随着应用的复杂性增加,我们会遇到一个常见的问题:多个异步请求串行执行可能导致性能瓶颈,进而影响用户体验。

背景

假设你正在开发一个需要获取多个数据源的页面,比如从数据库获取树形结构部门列表项目列表权限数据等。通常情况下,前端会发出多个异步请求去获取这些数据。以下是有问题的代码示例:

第一段代码:使用await
async handleAuthToolInfoPeople() {
  await this.fetchTreePeople();
  await this.fetchDeptList();
  await this.fetchProjectList();
  await this.fetchBaseList();
  await this.fetchAuthData();
}
关键点:

在这段代码中,多个 await 操作是串行执行的,意思是每个请求会依次等待上一个请求完成之后才能执行。如果每个请求需要的时间较长,那么整个页面加载的时间就会显著增加,进而影响用户体验。

关键点:
  • 没有并行化:这些请求会依次等待上一个请求完成之后才能执行。
  • 使用 await:使用 await 关键字来等待每个请求的完成,因此每个请求会按照顺序执行,如果每个请求需要的时间较长,那么整个页面加载的时间就会显著增加。
  • 等待:这段代码会确保所有请求完成后再进行后续操作
结果:
  • 异步请求等待:这些请求会启动,会阻止代码继续执行。
  • 请求串行的,代码逻辑不清晰:请求是异步的,按照控制流来确保执行顺序或获取结果。
问题:
  • 等待请求完成:如果有后续逻辑依赖这些请求的结果,可能会因为等待过长的时间。
第二段代码:没有 awaitPromise.all()
async handleAuthToolInfoPeople() {
  this.fetchTreePeople()
  this.fetchDeptList()
  this.fetchProjectList()
  this.fetchBaseList()
  this.fetchAuthData()
}
关键点:
  • 没有并行化:这些请求没有被包装成 Promise.all(),而是依次调用了每一个异步方法。每个异步方法会启动一个请求,但它们并不会并行执行。
  • 没有 await:没有使用 await 关键字来等待每个请求的完成,因此函数调用会立即返回,可能在请求完成之前就退出了。
  • 没有等待:这段代码没有确保所有请求完成后再进行后续操作。虽然每个请求会发起,但它们的执行顺序无法控制,且不会等待请求完成后再处理结果。
结果:
  • 异步请求不被等待:这些请求会启动,但不会阻止代码继续执行。如果你依赖于请求的结果来执行后续操作(例如将数据更新到 UI),这段代码可能会遇到问题,因为请求结果可能还未返回。
  • 请求依然是并行的,但代码逻辑并不清晰:实际上,虽然请求并行发起,但是并没有充分利用 async/await 来确保请求完成后的处理。换句话说,虽然请求是异步的,但没有控制流来确保执行顺序或获取结果。
问题:
  • 没有等待请求完成:如果有后续逻辑依赖这些请求的结果,可能会因为请求未完成而出错。
  • 代码逻辑不清晰:缺乏 awaitPromise.all() 的情况下,代码执行顺序可能不如预期,且没有明确的错误处理。

解决方案:并行化异步请求

如果多个请求之间没有依赖关系,我们就可以通过并行执行请求来减少总的等待时间。我们可以使用 JavaScript 中的 Promise.all() 方法来实现并行化请求。Promise.all() 方法接受一个数组,数组中的每一项都是一个返回 Promise 的异步操作。Promise.all()并行执行这些操作,直到所有操作都完成

优化后的代码如下:

async handleAuthToolInfoPeople() {
  // 并行执行多个请求,等待所有请求完成
  const fetchPromises = [
    this.fetchTreePeople(),
    this.fetchDeptList(),
    this.fetchProjectList(),
    this.fetchBaseList(),
    this.fetchAuthData(),
  ];

  try {
    // 等待所有请求都完成
    await Promise.all(fetchPromises);
  } catch (error) {
    console.error('Error fetching data:', error);
  }
}
async handleAuthToolInfoPeople() {
    this.fetchTreePeople()
    this.fetchDeptList()
    this.fetchProjectList()
    this.fetchBaseList()
    this.fetchAuthData()
}

关键点:

  • 并行请求:通过 Promise.all() 将多个异步请求并行执行。每个请求都会同时启动,而不是一个接一个地等待。
  • await Promise.all(fetchPromises)Promise.all() 等待所有的请求都完成。当所有的请求都完成时,控制权才会返回到 handleAuthToolInfoPeople() 函数继续执行。
  • 错误处理:如果有任何一个请求失败,Promise.all() 会立即抛出错误,并跳转到 catch 块。其他请求(即使它们成功完成)也会被中断。

优势:

  • 提高效率:所有请求是并行执行的,因此请求的总时长大大缩短,通常是取决于最长请求的时间,而不是所有请求的累加时间。
  • 适用于无依赖的请求:当多个请求之间没有依赖关系时,使用并行化可以最大化地提升性能。

为什么并行化可以提升性能?

在并行化请求之后,所有异步操作是同时发起的,减少了总的等待时间。传统的串行请求方式是一个请求完成后才会执行下一个请求,这意味着请求的总时间是所有请求时间的累加。而并行化请求后,总的请求时间就取决于耗时最长的单个请求,这样可以大幅度提升性能。

处理依赖关系:分组执行

在某些情况下,异步请求之间是有依赖关系的,比如 fetchAuthData 需要依赖其他数据才能执行。

在这种情况下,我们不能完全并行化所有请求。但是,我们仍然可以将没有依赖关系的请求进行并行化,等待这些请求完成后再执行依赖请求。这样可以进一步优化性能。

以下是基于依赖关系分组执行请求的优化示例:

async handleAuthToolInfoPeople() {
  // 并行执行与 fetchAuthData 无关的请求
  const fetchDataPromises = [
    this.fetchTreePeople(),
    this.fetchDeptList(),
    this.fetchProjectList(),
    this.fetchBaseList(),
  ];

  try {
    // 等待并行的请求完成
    await Promise.all(fetchDataPromises);
    // 执行依赖于其他数据的请求
    await this.fetchAuthData();
  } catch (error) {
    console.error('Error fetching data:', error);
  }
}

通过这种方式,我们在不影响业务逻辑的情况下,尽可能并行化请求,减少了用户等待的时间。

其他优化思路

  1. 缓存已请求的数据
    如果某些请求的数据是静态的或者不常变化的,我们可以考虑在前端缓存这些数据,避免每次都重新请求。比如,可以将数据存储在全局状态管理库(如 Vuex 或 Redux)中,或者本地存储(localStorage)中。
  2. 合并请求
    如果后端接口支持,可以考虑将多个请求合并成一个请求。这不仅可以减少请求的数量,还能减少网络传输的开销,进一步提升性能。
  3. 优化后端接口
    如果前端的优化措施不足以大幅提升性能,可能还需要考虑后端接口的优化。比如,后端能否通过批量查询的方式合并多个请求,返回合并后的数据?后端的响应速度也直接影响前端的性能。

总结

在前端开发中,当面对多个异步请求时,如何有效地管理这些请求以减少等待时间,显得尤为重要。通过并行化请求分组执行缓存已请求的数据,我们可以显著提升应用的响应速度和用户体验。


http://www.kler.cn/a/395965.html

相关文章:

  • leetcode hot100【LeetCode 236.二叉树的最近公共祖先】java实现
  • Postman接口测试(断言、关联、参数化、输出测试报告)
  • Android音视频直播低延迟探究之:WLAN低延迟模式
  • 排序算法 -快速排序
  • 项目技术栈-解决方案-web3去中心化
  • 网络技术-定义配置ACL规则的语法和命令
  • python isinstance(True, int)
  • Web_前端_HTML入门学习的案例案例1
  • 《动手学深度学习》中d2l库的安装以及问题解决
  • 免费送源码:Java+Springboot+MySQL Springboot多租户博客网站的设计 计算机毕业设计原创定制
  • 深度学习:transpose_qkv()与transpose_output()
  • taro框架h5项目打包后页面空白 解决办法
  • 【系统、用户提示词区别】
  • AI大模型(二):AI编程实践
  • 深度学习:广播机制
  • 差分数组-实现区间强度算法
  • Keil基于ARM Compiler 5的工程迁移为ARM Compiler 6的工程
  • 24.11.15 Vue3
  • Python进程间通讯大揭秘:原理深度剖析与实战案例分享
  • 数据网格能替代数据仓库吗?
  • 差分数组解析
  • golang中rpc
  • jmeter常用配置元件介绍总结之断言
  • 无人机图传系统介绍——CKESC电调小课堂11.0
  • 全面评估ASPICE标准对汽车软件开发的影响与效果
  • Android Studio | 修改镜像地址为阿里云镜像地址,启动App