簡體   English   中英

如何正確展平可觀察的 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ?

[英]How to properly flatten an stream of observables?

這是我想做的:

給定一個分頁的 API,使用並行請求獲取所有資源。

API 每次調用返回有限數量的資源。 因此,您需要使用偏移參數來獲取下一組數據,直到提取所有數據。

這是我的想法(但收到一些警告,因為我在響應中使用 flat),所以也許有更好的方法來做到這一點。

  1. 獲取項目的總數。
  2. 給定計數和限制,計算獲取所有數據需要多少請求。
  3. 並行觸發所有請求並將所有數據合並到一個扁平數組中。

這是一個例子:

https://stackblitz.com/edit/paginated-api?embed=1&file=index.ts&hideExplorer=1&devtoolsheight=100

getCount().pipe(
  mergeMap(count => range(0, Math.ceil(count / limit))),
  map(offset => getDevices(offset, limit)),
  combineAll(),
).subscribe(res => {
  const a = res.flat(); // <--- warning: Property 'flat' does not exist on type '{ name: string; }[][]'.
  console.log(JSON.stringify(a));
});

我覺得這個解決方案有點hacky。 它使訂閱中的響應變平。 我想知道是否有一個 RXJS 運算符可以在 pipe 上使用來展平響應,所以我不必在訂閱中?

對於每個內部 Observable,我們需要另一個展平運算符。

所以這樣的事情會起作用:

getCount().pipe(

  mergeMap(count => range(0, Math.ceil(count / limit))),

  mergeMap(offset => getDevices(offset, limit)),

  mergeAll(),
  toArray()

).subscribe(res => {
  console.log('result', JSON.stringify(res));
});

第一個mergeMap將內部range Observable 展平。 第二個mergeMapgetDevices展平,我假設它返回一個 Observable。

mergeAll()合並所有單獨的值,即對象。

然后toArray()將所有對象添加到單個數組中。

這是結果:

result
[{"name":"dev-1"},{"name":"dev-2"},{"name":"dev-3"},{"name":"dev-4"},{"name":"dev-5"},{"name":"dev-6"},{"name":"dev-7"},{"name":"dev-8"},{"name":"dev-9"},{"name":"dev-10"},{"name":"dev-11"},{"name":"dev-12"},{"name":"dev-13"},{"name":"dev-14"},{"name":"dev-15"},{"name":"dev-16"},{"name":"dev-17"},{"name":"dev-18"},{"name":"dev-19"},{"name":"dev-20"}]

希望這可以幫助。

3種方法來做到這一點:

使用mergeMap ,所有請求都是並行觸發的。 但是,最終結果將基於到達的順序。 這意味着如果您的 API 已排序,這可能會破壞它。

const getAllOffsets = () => pipe(
  mergeMap((count: number) => range(0, Math.ceil(count / limit))),
  toArray(),
  // all requests in parallel and results in order (order of creation)
  concatMap(r => forkJoin(...r.map(offset => getDevices(offset, limit)))),
  map(a => a.flat())
);

使用concatMap ,這將保證順序。 但是,每個請求都會一個接一個地執行。 這將是一個性能問題

const getAllOffsets2 = () => pipe(
  mergeMap((count: number) => range(0, Math.ceil(count / limit))),
  // all requests in parallel but order is not guarantee (response order)
  concatMap(offset => getDevices(offset, limit)),
  mergeAll(),
  toArray()
);

最后,使用forkJoin 這將並行執行所有請求並保持它們的創建順序。 Promise.all 這是從分頁 API 獲取所有資源的最佳選擇。

const getAllOffsets3 = () => pipe(
  mergeMap((count: number) => range(0, Math.ceil(count / limit))),
  toArray(),
  // all requests in parallel and results in order (order of creation)
  concatMap(r => forkJoin(...r.map(offset => getDevices(offset, limit)))),
  map(a => a.flat())
);

完整的工作示例: https://stackblitz.com/edit/paginated-api-ozcgwg?file=index.ts&hideExplorer=1&devtoolsheight=100

getCount().pipe(getAllOffsets3()).subscribe(res => {
  console.log({res, size: res.length || 0});
  console.log(JSON.stringify(res))
  // console.log('end', res.map(d => d.name));
});

暫無
暫無

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

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