Angular 4. A series of http requests get cancelled

2019-03-13 03:06发布

问题:

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.?

回答1:

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);
    });
}


回答2:

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.



回答3:

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)