12个例子掌握RxJs——1、expand

RxJS 实战:使用 expand 操作符实现链式递归请求

概述

在实际开发中,我们经常需要处理分页数据。有时候,我们需要一次性加载多页数据,而不是等待用户点击"下一页"。本章将介绍如何使用 RxJS 的 expand 操作符来实现链式递归请求,自动加载多页数据。

expand 操作符简介

expand 是 RxJS 中一个强大的操作符,它可以将一个 Observable 的值递归地展开为新的 Observable。每次展开时,我们可以根据当前值决定是否继续递归,或者返回 EMPTY 来终止递归。

基本语法

typescript 复制代码
source$.pipe(
  expand((value, index) => {
    // 根据 value 和 index 决定是否继续
    if (shouldContinue) {
      return nextObservable$;
    }
    return EMPTY; // 终止递归
  })
)

实战场景:递归加载分页数据

假设我们有一个分页 API,需要一次性加载前 10 页的数据。使用 expand 可以优雅地实现这个需求。

实现思路

  1. 发起第一页请求
  2. 使用 expand 递归地请求下一页
  3. 当达到指定页数时,返回 EMPTY 终止递归
  4. 使用 toArray() 收集所有响应
  5. 合并所有响应的数据

核心代码

typescript 复制代码
let currentPage = this.page;
const params = new HttpParams()
  .set('page', currentPage.toString())
  .set('pageSize', this.pageSize.toString());

this.http.get<ApiResponse>(this.apiUrl, { params })
  .pipe(
    expand((response, index) => {
      console.log('expand条件: index', index);
      // index 从 0 开始,所以 index >= 9 表示已经请求了10页(索引0-9)
      if (index >= 9) {
        return EMPTY; // 终止递归
      }
      // 递增页码
      currentPage = currentPage + 1;
      const nextParams = new HttpParams()
        .set('page', currentPage.toString())
        .set('pageSize', this.pageSize.toString());
      return this.http.get<ApiResponse>(this.apiUrl, { params: nextParams });
    }),
    toArray(), // 收集所有响应
    map((responses) => {
      // 合并所有响应的数据
      const allItems: DataItem[] = [];
      let total = 0;
      responses.forEach((response) => {
        if (response.success) {
          allItems.push(...response.data.items);
          total = response.data.pagination.total;
        }
      });
      return { items: allItems, total };
    })
  )
  .subscribe({
    next: (result) => {
      this.listOfData = result.items;
      this.total = result.total;
      console.log(`已加载 ${result.items.length} 条数据`);
    },
    error: (error) => {
      console.error('加载数据失败:', error);
    }
  });

关键点解析

1. expand 的 index 参数

expand 操作符的第二个参数 index 从 0 开始计数,表示当前是第几次展开(不包括初始值)。所以:

  • 第一次展开:index = 0(对应第 2 页)
  • 第二次展开:index = 1(对应第 3 页)
  • ...
  • 第九次展开:index = 9(对应第 11 页)

因此,要加载 10 页数据,条件应该是 index >= 9

2. 终止条件

当满足终止条件时,返回 EMPTY Observable,这会立即完成,不再继续递归。

3. toArray() 的作用

toArray() 会将 Observable 流中的所有值收集到一个数组中。这对于需要处理所有响应的情况非常有用。

4. 与 take() 的区别

虽然可以使用 take(10) 来限制请求次数,但更好的做法是在 expand 内部判断终止条件,这样可以更精确地控制递归逻辑。

优势

  1. 代码简洁 :使用 expand 可以避免复杂的循环和 Promise 链
  2. 自动处理:递归逻辑由 RxJS 自动管理,无需手动维护状态
  3. 易于扩展:可以轻松修改终止条件,比如根据响应数据决定是否继续

注意事项

  1. 内存占用:如果递归次数过多,可能会占用大量内存,需要注意
  2. 错误处理:如果中间某个请求失败,整个流会中断,需要适当的错误处理
  3. 性能考虑 :递归请求是串行的,如果数据量大,可能需要考虑并发请求(使用 forkJoin

总结

expand 操作符是处理递归请求场景的利器。通过它,我们可以优雅地实现链式递归请求,自动加载多页数据。在实际项目中,根据具体需求选择合适的策略,既能保证代码的可读性,又能满足性能要求。

相关推荐
yiludegeX3 个月前
fluth-vue: 体验流式编程范式之美
vue.js·前端框架·rxjs
一枚前端小能手3 个月前
「周更第5期」实用JS库推荐:RxJS
前端·javascript·rxjs
Wang's Blog6 个月前
Nestjs框架: RxJS 核心方法实践与错误处理详解
rxjs
jo7 个月前
RxJS 过滤运算符执行逻辑深度解析
rxjs
余生H1 年前
JS异步编程进阶(二):rxjs与Vue、React、Angular框架集成及跨框架状态管理实现原理
javascript·vue.js·react.js·angular·rxjs·异步编程
进二开物2 年前
原来 RxJS 是这样处理复杂数据流
前端·后端·rxjs
chuckchen2 年前
前端开发中的响应式编程
响应式编程·rxjs
进二开物2 年前
给 RxJS 的 Ajax 的功能拦截器
前端·javascript·rxjs
进二开物2 年前
为什么能用 RxJS 取代 Redux ?
前端·javascript·rxjs