Reputation: 7215
I have this logic:
public setXXXInLocalStorage(): Observable<boolean> {
return this.appConfig.getEndpoint('xxx')
.pipe(
switchMap((endpoint: Endpoint) => this.http.get(endpoint.toUrl(), {headers})),
map((body: any) => { localStorage.setItem('myItem', body.item); }),
timeout(5000),
map(() => true),
catchError(() => of(true))
);
}
What i wanted to achieve is, that i want to wait for max. 5 seconds for my get request.
What is now happening is: timeout waits always 5 seconds, even when my get request returns success in 1 sec.
Thanks in advance..
Upvotes: 2
Views: 3581
Reputation: 7215
i think this solved my problem: how to cancel RXJS subscribe after a while
From @Kroltan:
Apparently rxjs client keeps the observable running after receiving a value (presumably so you get notified of further updates). And since the Observable is never completed, Timeout will act after 30 (or whatever you pass as duration) seconds after receiving the data, causing the stream to fail.
To convert the "streaming" Observable into a single-event Observable, use the Take operator before timing out:
So i just needed to add a take(1) before my timeout.
public setXXXInLocalStorage(): Observable<boolean> {
return this.appConfig.getEndpoint('xxx')
.pipe(
switchMap((endpoint: Endpoint) => this.http.get(endpoint.toUrl(), {headers})),
tap((body: any) => { localStorage.setItem('myItem', body.item); }),
take(1),
timeout(5000),
map(() => true),
catchError(() => of(true))
);
}
Upvotes: 2
Reputation: 2758
When the timeout
operator follows the map
operator, the value returned from the callback to the map
operator is important as it determines what value is passed to the timeout
logic. Since there is no return value in your map
operation, then the timeout
operator is passed undefined
when it evaluates the Observable emission from the source (the HTTP get
).
Since an Observable of undefined
never completes, then the timeout
operator will throw an error always (after the time specified).
Instead of using map
for the side-effect of setting a value in local storage, use tap
which will just ferry the source observable on to the next operator in the pipe.
const endpoint = rxjs.of('blah');
function getRequest(httpObservable, name) {
return endpoint.pipe(
rxjs.operators.switchMap(() => httpObservable),
rxjs.operators.tap((val) => { // Call `tap` instead of `map`
console.log(`${name}: ${val}`);
}),
rxjs.operators.timeout(2000),
rxjs.operators.map(() => true),
).subscribe(
null,
() => console.log(`${name} Errored!`),
() => console.log(`${name} Complete!`)
);
}
const http = rxjs.timer(500);
const slowHttp = rxjs.timer(4000);
getRequest(http, 'Quick HTTP');
getRequest(slowHttp, 'Slow HTTP');
<script src="https://unpkg.com/@reactivex/[email protected]/dist/global/rxjs.umd.js"></script>
Upvotes: 0