When I try to make multiple http requests via http service in Angular 4,
previous request get cancelled in Chrome (but they reach the server).
Example:
const obs1 = this.http.get(`${API_URL}/transitions`);
const obs2 = this.http.get(`${API_URL}/states`);
obs1.subscribe();
obs2.subscribe(); // this will cancel obs1's http request
But if I replace .subscribe()
to .publish().connect()
like above, it will work correctly (no cancels)
const obs1 = this.http.get(`${API_URL}/transitions`);
const obs2 = this.http.get(`${API_URL}/states`);
obs1.publish().connect();
obs2.publish().connect();
Or if I merge two Observables
to one and then get subscribed like above, it will work correctly too
const obs1 = this.http.get(`${API_URL}/transitions`);
const obs2 = this.http.get(`${API_URL}/states`);
Observable.merge(obs1, obs2).subscribe()
Why do I face this behavior? I need to understand, not bypass. How can I make series of requests without merging, forking etc.?
I've found a potential reason of this behavior.
Thanks to https://github.com/ghetolay and https://github.com/dklmuc
We found out that angular will cancel very fast http requests which without any callback. So, you have to pass onNext in subscribe
From https://github.com/ghetolay:
ok it's race condition I think
it will always call xhr.abort() on teardown
if the connection is still considered open by the browser it will close it, otherwise probably doing nothing
so when you have a very fast handling of the response (like no callback is really really fast) it may abort the connection that's still considered open
This one works correctly:
for (let i = 1; i < 50; i++) {
http.get(`https://swapi.co/api/people/${i}`).subscribe((result) => {
console.log(i, result);
});
}
This has to do with observables being cancelled, although I must admit I don't know the root cause for the problem you face.
I ran into a similar problem using the ngrx/effects module. The action handler made requests to a service using ngrx switch map operator. All except the last request would get cancelled. Changing it to merge map fixed the problem.
Below is the code
@Effect()
loadSomeData$: Observable<Action> = this.actions$
.ofType(SomeActions.Load_Data)
.mergeMap((id) =>
this.someService.getSomething(id)
.map((someEntity) => new SomeActions.LoadDataSuccess(someEntity)
).catch((x, y) => {
console.error('Error occured');
return Observable.of(new SomeActions.LoadDataFailed(id));
}
));
Reproducing the relevant part of ngrx here.
https://www.learnrxjs.io/operators/transformation/switchmap.html
The main difference between switchMap and other flattening operators is the cancelling effect. On each emission the previous inner observable (the result of the function you supplied) is cancelled and the new observable is subscribed. You can remember this by the phrase switch to a new observable.
For me the problem was because of an empty observable.
I was using Observable.forkJoin
on a list of observables.
SOLUTION: I needed to give my Observable.of()
a value: Observable.of(null)