[英]In Rxjs, how do I flatten or merge a stream containing both normal types and Observables?
[英]How to properly flatten an stream of observables?
這是我想做的:
給定一個分頁的 API,使用並行請求獲取所有資源。
API 每次調用返回有限數量的資源。 因此,您需要使用偏移參數來獲取下一組數據,直到提取所有數據。
這是我的想法(但收到一些警告,因為我在響應中使用 flat),所以也許有更好的方法來做到這一點。
這是一個例子:
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 展平。 第二個mergeMap
將getDevices
展平,我假設它返回一個 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.