首先:这是我使用RxJs的第一个项目,我想我会用它来学习。
我找到了这个答案:Turning paginated requests into an Observable stream with RxJs 但它在评论中说:
您仍然超过最大调用堆栈。大约430页返回。我认为递归可能不是最好的解决方案
我想查询Youtube Data API,结果以页面形式返回,我需要对它们进行分页。 我想像这样的工作流程可以工作: 1)发起呼叫 2)检查响应是否有' nextPageToken' 3)如果有,请对Youtube API再做一次请求 4)如果没有,请完成
So to do this I could Imagine the following Observables / streams:
FirstRequestStream -A-X--------------->
ResponseStream -A-A-A-A--X-------->
RequestStream -I-A-I-A----------->
A = Action
I = Info from upper stream
X = Termination
(不确定这个图表是否与我的方式一致)
因此ResponseStream依赖于FirstRequestStream和RequestStream(使用merge函数)。 RequestStream依赖于ResponseStream(这被称为循环可观察?)
- 这是正确的做法吗?
-Are'流通的观察者'好事,他们甚至可能吗?(我创建一个问题)。
- 我应该先尝试其他方式吗?
- 是否可以创建相互依赖的可观察流?
感谢您的帮助。
答案 0 :(得分:23)
您使用延迟运算符可以更轻松地解决这个问题。
想法是你正在创建延迟的observable(因此它将被创建并仅在订阅后开始获取数据)并将其与相同的observable连接,但是对于下一页,它也将与下一页连接,所以上 ... 。所有这一切都可以在没有递归的情况下完成。
以下是代码的外观:
<script src="https://unpkg.com/rxjs@6.2.2/bundles/rxjs.umd.min.js"></script>
&#13;
{{1}}&#13;
答案 1 :(得分:2)
我无耻地重复使用了Oles Savluk具有出色的fetchPage
功能的代码段,并使用expand
应用了Picci链接到的博客文章中解释的思想(在评论中)。
Article on expand by Nicholas Jamieson
它提供了一个稍微简单的代码,在expand
调用中隐藏了递归(如果需要,文章的注释显示了如何线性化它。)
const { from, concat, empty, timer } = rxjs; // = require("rxjs")
const { concatMap, expand, mapTo, tap, toArray } = rxjs.operators; // = require("rxjs/operators")
// simulate network request
const pageNumber = 3;
function fetchPage(page = 0) {
return timer(1000).pipe(
tap(() => console.log(`-> fetched page ${page}`)),
mapTo({
items: Array.from({ length: 10 }).map((_, i) => page * 10 + i),
nextPage: ++page === pageNumber ? undefined : page,
}),
);
}
const list = fetchPage().pipe(
expand(({ nextPage }) => nextPage ? fetchPage(nextPage) : empty()),
concatMap(({ items }) => items),
// Transforms the stream of numbers (Observable<number>)
// to a stream with only an array of numbers (Observable<number[]>).
// Remove if you want a stream of numbers, not waiting for all requests to complete.
toArray(),
);
list.subscribe((items) => console.log(items));
<script src="https://unpkg.com/rxjs@6.2.2/bundles/rxjs.umd.min.js"></script>
答案 2 :(得分:2)
这是我的解决方案,使用HttpClient模块在Angular类上下文中使用rxjs运算符expand
,reduce
和empty
:
假设您的API响应是一个对象,其中包含属性“数据”(结果项的数组)和“下一个”(字符串,包含下一页的url,如果没有更多下一个项,则为null) )。
getAllResults(url) {
return this.http.get(url).pipe(
expand((res) => res.next ? this.http.get(next) : EMPTY),
reduce((acc, res) => acc.concat(res.data), [])
);
}
答案 3 :(得分:0)
LuJaks 绝对是最简单的方法!
对于单行示例,假设您有一个函数对给定页面发出 http 请求并返回(部分)数据数组。我们调用该函数直到服务器返回空数组:
import { Observable, EMPTY, of } from "rxjs";
import { expand, reduce } from "rxjs/operators";
// Mock a request that returns only 5 pages...
function httpGet(p): Observable<number[]> {
if (p > 5) { return of([]); }
return of(new Array(10).fill(0).map((_, i) => p * 10 + i));
}
httpGet(0).pipe( // get the fist page
expand((value, index) => (value.length > 0 ? httpGet(index + 1) : EMPTY)), // other pages
reduce((a, v) => [...a, ...v], []), // optional if you want only one emit
).subscribe((x) => console.log(x));