简体   繁体   English

RxJS:使用 NestJS HttpService 对分页 API 的并行 http 调用

[英]RxJS: Parallel http call to paginated API using NestJS HttpService

Im using NestJS, and this is my current implementation to make parallel http request:我正在使用 NestJS,这是我当前用于发出并行 http 请求的实现:

@Injectable()
export class AppService {
  constructor(private readonly http: HttpService) {}

  private fetchData(index: number) {
    Logger.log(`Collect index: ${index}`);

    return this.http
      .get<Passenger>(
        `https://api.instantwebtools.net/v1/passenger?page=${index}&size=100`,
        { validateStatus: null },
      )
      .pipe(concatMap(response => of(response.data)));
  }

  async getAllData() {
    let a = 0;
    const collect: Passenger[] = [];
    const $subject = new BehaviorSubject(a);

    await $subject
      .pipe(
        flatMap(index =>
          forkJoin([
            this.fetchData(index),
            this.fetchData(index + 1),
            this.fetchData(index + 2),
          ]).pipe(mergeAll()),
        ),
        tap(data => {
          collect.push(data);

          if (data?.data?.length === 0) {
            $subject.complete();     // stop the stream
          } else {
            a += 3;     // increment by 3, because the request is 3 times at a time
            $subject.next(a);
          }
        }),
      )
      .toPromise();

    return collect;
  }
}

This service is to collect the 3rd party data.此服务是为了收集第 3 方数据。 As for now, the fetchData() function is called multiple times according to how many parallel requests I want at a time.至于现在,根据我一次想要多少并行请求,多次调用fetchData()函数。 I use a dummy API for the test, but in the real scenario the API endpoint size limit is 100 and it doesn't return the meta information about how much the totalPage.我使用了一个虚拟 API 进行测试,但在实际场景中,API 端点大小限制为 100,并且它不返回有关 totalPage 的元信息。 It just returns the empty data when the last page is reached.它只是在到达最后一页时返回空数据。

The goal is to make a parallel request and combine the result at the end.目标是发出并行请求并在最后合并结果。 I'm doing this to keep the request time as minimum as possible and because the API itself has a rate limit of 50 requests per second.我这样做是为了尽可能缩短请求时间,因为 API 本身的速率限制为每秒 50 个请求。 How to optimize this code?如何优化这段代码?

To fetch all pages in one go you can use expand to recursively subscribe to an observable that fetches some pages.要一次性获取所有页面,您可以使用expand来递归订阅一个可获取某些页面的 observable。 End the recursion by returning EMPTY when the last page you received is empty.当您收到的最后一页为空时,通过返回EMPTY结束递归。

function fetchAllPages(batchSize: number = 3): Observable<any[][]> {
  let index = 0;
  return fetchPages(index, batchSize).pipe(
    // if the last page isn't empty fetch the next pages, otherwise end the recursion
    expand(pages => pages[pages.length - 1].length > 0 
      ? fetchPages((index += batchSize), batchSize) 
      : EMPTY
    ),
    // accumulate all pages in one array, filter out any trailing empty pages
    reduce((acc, curr) => acc.concat(curr.filter(page => page.length)), [])
  );
}

// fetch a given number of pages starting from 'index' as parallel requests
function fetchPages(index: number, numberOfPages: number): Observable<any[][]> {
  const requests = Array.from({ length: numberOfPages }, (_, i) =>
    fetchData(index + i)
  );
  return forkJoin(requests);
}

https://stackblitz.com/edit/rxjs-vkad5h?file=index.ts https://stackblitz.com/edit/rxjs-vkad5h?file=index.ts

This will obviously send a few unnecessary requests in the last batch if这显然会在最后一批中发送一些不必要的请求,如果
(totalNumberOfPages + 1) % batchSize != 0 . (totalNumberOfPages + 1) % batchSize != 0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM