簡體   English   中英

RxJS:使用 NestJS HttpService 對分頁 API 的並行 http 調用

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

我正在使用 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;
  }
}

此服務是為了收集第 3 方數據。 至於現在,根據我一次想要多少並行請求,多次調用fetchData()函數。 我使用了一個虛擬 API 進行測試,但在實際場景中,API 端點大小限制為 100,並且它不返回有關 totalPage 的元信息。 它只是在到達最后一頁時返回空數據。

目標是發出並行請求並在最后合並結果。 我這樣做是為了盡可能縮短請求時間,因為 API 本身的速率限制為每秒 50 個請求。 如何優化這段代碼?

要一次性獲取所有頁面,您可以使用expand來遞歸訂閱一個可獲取某些頁面的 observable。 當您收到的最后一頁為空時,通過返回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

這顯然會在最后一批中發送一些不必要的請求,如果
(totalNumberOfPages + 1) % batchSize != 0

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM