自学内容网 自学内容网

提升前端性能:如何优化多个异步请求的执行效率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. 优化后端接口
    如果前端的优化措施不足以大幅提升性能,可能还需要考虑后端接口的优化。比如,后端能否通过批量查询的方式合并多个请求,返回合并后的数据?后端的响应速度也直接影响前端的性能。

总结

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


原文地址:https://blog.csdn.net/qq_47658204/article/details/143772648

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!