我需要调用api并从端点获取所有项目。但是,我们一次只能获取100个项目。
响应如下:
{
elements: Array<any>,
totalCount: number,
}
,端点为/api/items?drop=${drop}&take=100
,drop
用于分页。
我认为这将涉及扫描和takeWhile操作员某处。这就是我得到的:
const subject = new Subject();
const rec = subject.pipe(
scan((acc: number, curr: number) => (curr + 100), 0),
switchMap(drop => this.http.get(`api/items?drop=${drop}&take=100`)),
takeWhile((r: any) => r.elements.length === 100),
tap(r => subject.next())
);
答案 0 :(得分:2)
您不需要知道将返回多少元素,只需要返回多少元素:)
Observable.range(0, 1000000).pipe(
concatMap(page => this.http.get(`api/items?drop=${page * take}&take=${take}`))
takeWhile(results => results.elements.length === take)
)
起初我认为使用range
作为源会堆积请求并破坏分页的目的,但concatMap会自动限制。
来自learnrxjs.io/operators/transformation/concatmap
注意concatMap和mergeMap之间的区别。因为concatMap在前一个完成之前没有订阅下一个observable,所以将首先发出源的值延迟2000ms。将此与立即订阅内部可观察量的mergeMap相比较,具有较小延迟(1000ms)的观察者将发射,接着是需要2000ms完成的观察者。
这是对 concatMap 的测试,显示在前一个结果发出之前不会调用getPage
const take = 100;
const getPage = (page) => {
console.log('reading page', page);
return Rx.Observable.of(page).delay(1000);
}
Rx.Observable.range(0,3)
.concatMap(getPage)
.subscribe(results => console.log('concatMap results', results));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.10/Rx.js"></script>
与 mergeMap 对比,后者立即调用getPage
const take = 100;
const getPage = (page) => {
console.log('reading page', page);
return Rx.Observable.of(page).delay(1000);
}
Rx.Observable.range(0,3)
.mergeMap(getPage)
.subscribe(results => console.log('mergeMap results', results));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.10/Rx.js"></script>
答案 1 :(得分:1)
似乎我通过使用外部变量让它工作,但它并不是真的很漂亮......
const take = 100;
const subject = new BehaviorSubject(0);
// var used for pagination
let i = 0;
const rec = subject.pipe(
map((acc: number) => (acc * 100)),
switchMap(drop => this.http.get(`api/items?drop=${drop}&take=${take}`)),
map((r: any) => r.elements),
tap(items=> {
if (items.length === take)
subject.next(++i);
})
);
return rec;
答案 2 :(得分:1)
希望这会有所帮助:)
我会选择更简单的东西,订阅有点必要,但我想我们可以为了可读性而牺牲幻想。您应该考虑错误和重试,因为这显然只是快乐的路径
const subject = new Subject();
const createUrl = (drop = 0) => `api/items?drop=${drop}&take=100`
const fetchApi = url => Observable.fromPromise(fetch(url))
const requestItems = (pagination = 0) => {
fetchApi(createUrl(pagination))
.take(1)
.subscribe(response => {
if(response.elements.length === 100) {
requestItems(pagination++)
}
subject.next(response)
})
}
requestItems()
答案 3 :(得分:1)
阐述@cartant的建议,您可以满足您的需求,而不必依赖于主题,而是使用expand
运算符,沿着以下代码段的方向
let counterOfItemsFetched = 0;
function api() {
// console.log('counterOfItemsFetched', counterOfItemsFetched)
return counterOfItemsFetched < 1000 ? 100 : 1;
}
function apiAsynCall() {
return Observable.of(api()).delay(1000);
}
Observable.of(0).pipe(
expand(() => apiAsynCall().pipe(tap(itemsFetched => counterOfItemsFetched = counterOfItemsFetched + itemsFetched))),
filter(counter => counter > 0),
tap(() => console.log(counterOfItemsFetched)),
takeWhile(counter => counter === 100),
)
.subscribe(
null,
null,
() => console.log('counter value at the end', counterOfItemsFetched)
)