我试图弄清楚如何在映射数据并使用RxJS运算符减少数据的同时每五秒钟发送一次AJAX请求。
因此,有一个简单的Angular服务,它为目前在太空中的宇航员请求外部API:http://api.open-notify.org/astros.json
在服务中,我尝试先设置interval(5000)
,然后使用RxJS映射运算符将传入的号码映射到GET请求。
任务是这样的:我需要首先找到宇航员,然后找到那些正在乘坐国际空间站飞行的人,然后再将那些我可以渲染到视图中的宇航员。数据必须每5秒更新一次;因此我需要每5秒重新发送一次相同的HTTP请求,这可以通过setInterval()
完成。这不是解决问题的最佳方法。
代码如下:
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError, interval } from 'rxjs';
import { catchError, filter, concatMap, flatMap, map, reduce } from 'rxjs/operators';
import { Astronaut } from '../models/astronaut.model';
const astronautsUri = 'http://api.open-notify.org/astros.json';
@Injectable({
providedIn: 'root'
})
export class IssLocatorService {
constructor(private http: HttpClient) {}
getAstronauts(): Observable<Astronaut[]> {
return interval(5000).pipe(
concatMap(num => this.http.get(astronautsUri)),
flatMap((newAstronauts: any) => newAstronauts.people),
filter((astronaut: Astronaut) => astronaut.craft === 'ISS'),
map((astronaut: Astronaut) => [astronaut]),
reduce((prev, next) => [...prev, ...next]),
catchError(this.handleError)
);
}
}
代码不起作用,a。
尽管流程确实到达了reduce()
运算符,但该运算符不会返回组件中的subscribe()
方法。
但是,在我看来,该解决方案应该可以正常工作。这是我认为的工作方式:
concatMap()
等到内部Observable完成,然后才获得第二个数字-1。{success:true, people: [...]}
),然后使用flatMap()
将该物体转换为宇航员数组people
。由于people
的工作原理,flatMap()
中的每个对象随后变为可观察对象。astronauts.people
多亏了reduce()
。reduce()
应该返回到subscribe
,因为内部Observable已完成。 但不是:reduce()
等到interval()
产生下一个数字并将其映射到内部Observable时,再次返回people
并将其推送到相同的数组。并持续不断。如果我将reduce()
替换为scan
,则阵列或宇航员会返回到subscribe
方法。但是,由于宇航员反复将物体推入其中,该阵列不断变大。
以下方法可以正常工作:
return this.http.get(astronautsUri).pipe(
flatMap((newAstronauts: any) => newAstronauts.people),
filter((astronaut: Astronaut) => astronaut.craft === 'ISS'),
map((astronaut: Astronaut) => [astronaut]),
reduce((prev, next) => [...prev, ...next]),
catchError(this.handleError)
);
但是在这种情况下,我必须在呈现宇航员的组件类中使用setInterval()
手动设置间隔,并且必须调用getAstronauts()
方法。因此,基本上ngOnInit
中有两次方法调用。
仅使用RxJS运算符怎么能达到预期的效果?我想设置一个间隔,映射并过滤并减少对象的数组,然后接收它们。
我对RxJS映射如何工作的理解确实很糟糕,但是我尝试了(为了尝试)所有这些方法-switchMap()
,concatMap()
,exhaustMap()
,{{1} }-将flatMap()
中的数字映射到AJAX请求。仍然不起作用。
答案 0 :(得分:0)
我认为您要实现的目标是
getAstronauts(): Observable<Astronaut[]> {
return interval(5000).pipe(
concatMap(num =>
this.http.get(astronautsUri).pipe(
flatMap((newAstronauts: any) => newAstronauts.people),
filter((astronaut: Astronaut) => astronaut.craft === 'ISS'),
reduce((prev, astronaut) => [...prev, astronaut], []),
)
),
catchError(this.handleError)
);
}
reduce方法的问题是它在发出任何值之前正在等待可观察到的源完成。在我的代码中,源是一个请求的元素,有问题的示例中,源是所有请求的所有元素,永远不会结束
答案 1 :(得分:0)
更改reduce进行扫描,scan与reduce进行相同的操作,但是在每次迭代时发出,reduce仅在整个流完成后才发出。
const { from } = rxjs;
const { scan } = rxjs.operators;
const obs$ = from([1,2,3,4,5]);
obs$.pipe(scan((total, item) => total + item)).subscribe(val => { console.log(val); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.4.0/rxjs.umd.min.js"></script>
const { from } = rxjs;
const { reduce } = rxjs.operators;
const obs$ = from([1,2,3,4,5]);
obs$.pipe(reduce((total, item) => total + item)).subscribe(val => { console.log(val); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.4.0/rxjs.umd.min.js"></script>