我有一个用例,每当触发新请求时,任何已经在进行中的http请求都应该被取消/忽略。
例如:
- 请求(比如#2)进来,而请求#1 响应时间太长/网络连接速度慢。
- 在这种情况下,#2 从服务器获得非常快的响应,即使 #1 返回 HTTP 响应 可观察的应该被忽略。
- 我面临的问题是,首先组件显示请求 #2 的响应值,并在请求 #1 完成时再次更新(这不应该发生)。
我认为 switchMap 取消了可观察值/维持了可观察值的发出顺序。
摘自我的服务。ts
Obervable.of('myServiceUrl')
.switchMap(url => this.httpRequest(url) )
.subscribe( response => {
// implementation
/** Update an observable with the
with latest changes from response. this will be
displayed in a component as async */
});
private httpRequest(url) {
return this.httpClient.get('myUrl', { observe: 'response' });
}
上面的实现不起作用。有人能找出这个用例的正确实现吗?
您似乎正在创建多个可观察对象。从您的示例中不清楚,但似乎您每次想要提出请求时都会调用
Observable.of
。这每次都会创建一个 new Observable 流,因此对于每个后续调用,您都会获得一个新流,并且前一个流不会被取消。这就是 .switchMap
不起作用的原因。
如果您希望
.switchMap
取消 HTTP 请求,则需要它们使用 same 可观察流。您想要使用的源 Observable 取决于触发 http 请求的具体内容,但您可以使用类似 Subject
之类的内容自行管理。
const makeRequest$ = new Subject();
const myResponse$ = makeRequest$.pipe(switchMap(() => this.service.getStuff()));
您可以订阅
myResponse$
以获得回复。任何时候您想触发请求,都可以执行makeRequest$.next()
。
我有以下代码摘录,其中 switchMap 实现成功。
class MyClass {
private domain: string;
private myServiceUri: subject;
myData$: Observable<any>;
constructor(private http: HttpClient) {
.....
this.myServiceUri = new Subject();
this.myServiceUri.switchMap(uri => {
return this.http.get(uri , { observe: 'response' })
// we have to catch the error else the observable sequence will complete
.catch(error => {
// handle error scenario
return Obervable.empty(); //need this to continue the stream
});
})
.subscribe(response => {
this.myData$.next(response);
});
}
getData(uri: string) {
this.myServiceUri.next(this.domain + uri); // this will trigger the request
}
}
一种方法可能看起来与上面的答案相似,但实现方式不同。
在下面的示例中,您可以看到如果多次调用,API/http 调用会被取消。目的是避免 API 调用解析,最终导致浏览器响应缓慢。(例如:Typeahead)
switchMap 一次只能容纳一个可观察对象。因此,它取消了其余的 API 调用。
switch-map.component.ts
import { Component, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'switch-map',
templateUrl: './switch-map.component.html'
})
export class SwitchMapComponent implements OnInit {
subject: Subject<string> = new Subject()
constructor(private http: HttpClient) { }
makeApiCall(): void {
this.subject.next('https://jsonplaceholder.typicode.com/photos')
}
ngOnInit(): void {
this.subject.pipe(switchMap(url => this.http.get(url)))
.subscribe(data => console.log('API Response: ', data))
setInterval(() => {
document.getElementById('btn')?.click()
}, 10)
}
}
switch-map.component.html
<button id="btn" (click)="makeApiCall()">Click</button>