我有一个基于参数执行http请求的函数。我想添加某种" debounce"功能。因此,如果在设定的时间窗口中多次调用该函数,我想将参数组合成一个请求,而不是发出多个请求。
我想用Observables和Angular来实现这个目标。这听起来并不复杂,但是我无法让它运转起来,也许我错过了一些东西。
现在让我们在单个请求中跳过组合,因为这可以通过聚合去抖或Oberservable.buffer来完成。我在组合单个Observable时遇到了麻烦。
这是我迄今为止所尝试的内容。
我尝试使用主题,因为这似乎是这种情况的正确对象(https://stackblitz.com/edit/angular-hcn41v?file=src%2Fapp%2Fapp.component.ts)。
constructor(private http: HttpClient) {
this.makeRequest('1').subscribe(x => console.log(x))
this.makeRequest('2').subscribe(console.log)
setTimeout(() => {
this.makeRequest('3').subscribe(console.log)
}, 1000)
}
private makeRequest(id: string) {
this.observable = this.observable.pipe(
merge(Observable.of(id).pipe(delay(1)))
)
return this.aggregateDebounce(this.observable)
}
private getUrl(value) {
console.log('getUrl Call', value);
return 'https://jsonplaceholder.typicode.com/posts/1';
}
private aggregateDebounce(ob$) {
const shared$ = ob$.publishReplay(1).refCount()
return shared$.buffer(shared$.debounceTime(75))
}
我希望有一个' getUrl Call'记录每个函数调用和一个结果日志。但是,如果我向this.makeRequest()添加超过1次调用,我只会得到结果,结果也很奇怪。始终返回所有先前的值。我想我并不完全理解在这种情况下主题是如何运作的。
另一种方法(取自RXJS: Aggregated debounce)是为了创造某种聚合去抖(https://stackblitz.com/edit/angular-mx232d?file=src/app/app.component.ts)
constructor(private http: HttpClient) {
this.makeRequest('1').subscribe(x => console.log(x))
this.makeRequest('2').subscribe(console.log)
setTimeout(() => {
this.makeRequest('3').subscribe(console.log)
}, 1000)
}
private makeRequest(id: string) {
this.observable = this.observable.pipe(
merge(Observable.of(id).pipe(delay(1)))
)
return this.aggregateDebounce(this.observable)
}
private getUrl(value) {
console.log('getUrl Call', value);
return 'https://jsonplaceholder.typicode.com/posts/1';
}
private aggregateDebounce(ob$) {
const shared$ = ob$.publishReplay(1).refCount()
return shared$.buffer(shared$.debounceTime(75))
}
在这种情况下,我遇到的问题是我也获得了以前的所有值。
理论上(至少对我而言)两种变体听起来都是合理的,但似乎我错过了一些东西。任何对正确方向的眨眼都非常感谢。
修改:
根据要求,我添加了最终的实际目标。
想象一下从API请求信息的服务。在50-75ms内,您可以使用特定ID来呼叫服务。我想将这些ID组合到一个请求而不是3。如果100ms之后再次调用该服务,则会发出新的请求
答案 0 :(得分:1)
this.makeRequest(1).subscribe();
private makeRequest(number: number) {
this.values.next(number);
return this.idObservable.pipe(
您在订阅前发出该值 - >价值会丢失。
private values: Subject = new Subject();
private idObservable = this.values.pipe(
private makeRequest(number: number) {
this.values.next(number);
return this.idObservable.pipe(
每次调用都会根据主题创建一个新的observable。每当您发出一个值时,所有订阅者都会收到该值。
可能的解决方案看起来像这样(我在这里使用新的rxjs语法):
subject: Subject<String> = null;
observable = null;
window = 100;
constructor() {
this.subject = null;
this.window = 100;
this.makeRequest('1').subscribe(console.log)
this.makeRequest('2').subscribe(console.log)
setTimeout(() => {
this.makeRequest('3').subscribe(console.log)
}, 1000)
}
private makeRequest(id: string) {
if (!this.subject) {
this.subject = new ReplaySubject()
this.observable = this.subject.pipe(
takeUntil(timer(this.window).pipe(take(1))),
reduce((url, id, index) => this.combine(url, id), baseUrl),
flatMap(url => this.request(url)),
tap(() => this.subject = null),
share()
)
}
this.subject.next(id);
return this.observable;
}
combine
创建网址,request
发出实际请求。
答案 1 :(得分:1)
Rxjs非常擅长处理这种情况。您将需要两个不同的主题:
发出请求时,该值将被推到第一个主题上,而第二个主题将被返回,从而抽象出组合请求的内部逻辑。
private values: Subject = new Subject();
private results: Subject = new Subject();
private makeRequest(number: number) {
this.values.next(number);
return this.results;
}
根据需要,合并请求的管道可以是问题中的buffer
和debounceTime
或其他逻辑。收到响应后,只需将其推入结果主题:
constructor(private http: HttpClient) {
this.values
.pipe(
buffer(this.values.pipe(debounceTime(1000))),
switchMap(values => this.getUrl(values)),
map(response => this.results.next(response)))
.subscribe();
}
在将响应推送到结果上之前,我曾使用switchMap
模拟异步请求。