Angular NGRX 将大型数组拆分为块并合并 API 请求结果

Angular NGRX split large array to chunks and combine API requests results

所以问题是我有大量的对象,比方说我需要在 NGRX 效果中发送到我们的 API 的 1000,然后用响应发送一个新的 Action,但是 API 一次处理这么大的请求需要很长时间,所以我想把这个数组分成更小的块,一个接一个地发送一个请求,然后将所有请求的响应组合成一个动作。我尝试过使用 from() 运算符将大量数据映射到 Observables 数组中,但在映射后无法弄清楚接下来要使用什么,我尝试将其转换为 Promises 并使用Promise.all() 在他们身上,但又一次,运气不好。总之,我需要做的是:

  1. Action 触发需要发送 API 调用的 Effect,调用来自 Action 的一组数据,
  2. 在 Effect 中,数据数组被分成 100 个对象的块,
  3. 这些chunk被映射成API个请求,一个接一个发送,每个的响应需要和前面的响应结合起来,
  4. 完成所有这些请求后,需要分派一个包含来自它们的组合响应的 Action 来更新 Store。

这是我目前正在使用的简化效果,我需要划分的是action.rows:

loadRows$ = createEffect(() => {
  return this.actions$.pipe(
    ofType(RowsActions.LoadRows),
    switchMap((action) => {
      return this.eventsService.getBatchRows(action.rows).pipe(
        switchMap((response) => {
          return [
            new RowsLoaded({ rows: response.rows }),
            new LoadedTableData(response.rows),
          ];
        }),
        catchError(() => {
          return of(new RowsFailedToLoad());
        })
      );
    })
  );
});

已经实现了类似的逻辑, 如果一个块将有 100 个项目 - 它仍然很大。

我是 运行 并行的所有项目。

更新:

如果您将 mergeMap 更改为 concatMap - 所有调用都将 顺序

不管是单行还是多行

        from(action.rows)
            .pipe(
                mergeMap(id => api(id).pipe(catchError(e => EMPTY))),
                toArray())
             )

这里是工作示例:https://codesandbox.io/s/rxjs-playground-forked-u8c7jd?file=/src/index.js

最终版本:

const { of, from } = require("rxjs");
const {
  switchMap,
  concatMap,
  toArray,
  catchError,
  map,
  tap
} = require("rxjs/operators");

const chunkArray = (arr, size) =>
  arr.length > size
    ? [arr.slice(0, size), ...chunkArray(arr.slice(size), size)]
    : [arr];
const getBatchRows = (rows) => of({ response: { rows } });

of({ rows: ["id1", "id2", "id3", "id4", "id5"] })
  .pipe(
    switchMap((action) =>
      from(chunkArray(action.rows, 2))
        .pipe(
          concatMap((rows) =>
            getBatchRows(rows).pipe(catchError((e) => of({})))
          )
        )
        .pipe(toArray())
    ),

    tap((data) => console.log(data)),
    map((responseArray) => responseArray.map((i) => i.response.rows).flat()),
    map(rows => [
      new RowsLoaded({ rows }),
      new LoadedTableData(rows),
    ])
  )
  .subscribe((data) => {
    console.log(data);
  });

使用块方式——几乎无事可做

将使用

const chunkArray = (arr, size) =>
  arr.length > size
    ? [arr.slice(0, size), ...chunkArray(arr.slice(size), size)]
    : [arr];

并在此处使用:

 from(chunkArray(action.rows, 100))

//and change this
 mergeMap(rows => this.eventsService.getBatchRows(rows)