问题 Angular 4.一系列http请求被取消


当我尝试通过Angular 4中的http服务发出多个http请求时, 之前的请求在Chrome中被取消(但它们到达服务器)。 例:

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

但如果我更换 .subscribe() 至 .publish().connect() 像上面一样,它会正常工作(没有取消)

const obs1 = this.http.get(`${API_URL}/transitions`);
const obs2 = this.http.get(`${API_URL}/states`);
obs1.publish().connect();
obs2.publish().connect();

或者如果我合并两个 Observables 到一个,然后像上面那样订阅,它也将正常工作

const obs1 = this.http.get(`${API_URL}/transitions`);
const obs2 = this.http.get(`${API_URL}/states`);
Observable.merge(obs1, obs2).subscribe()

为什么我会面对这种行为?我需要了解,而不是绕过。 我该如何提出一系列请求  合并,分叉等?


12822
2017-08-23 15:56


起源



答案:


我发现了这种行为的潜在原因。

谢谢 https://github.com/ghetolay 和 https://github.com/dklmuc 

我们发现angular会取消非常快速的http请求,而不需要任何回调。所以,你必须在订阅中传递onNext

https://github.com/ghetolay

好吧,我认为这是竞争条件

它总是会在拆解时调用xhr.abort()

如果浏览器仍认为连接已打开,它将关闭它,否则可能什么都不做

所以当你对响应进行非常快速的处理时(比如没有回调真的很快),它可能会中止仍然被认为是开放的连接

这个工作正常:

for (let i = 1; i < 50; i++) {
    http.get(`https://swapi.co/api/people/${i}`).subscribe((result) => {
        console.log(i, result);
    });
}

8
2017-08-25 13:21



现在我感觉很糟糕,因为我要对此发表评论,但只是假设你的例子已经简化了。如果您需要在没有订阅的情况下执行http请求。你可以打电话 http.get(...).take(1) 在获取第一个项目后,它将订阅并销毁订阅。 - cgTag
@ctTag是(1)将调用请求事件而不使用subscribe方法? - WimmDeveloper
@ctTag不,它不会。现在检查。但我刚刚意识到,使用http方法非常合理 .take(1) - WimmDeveloper
非常感谢引用我,但这只是一个疯狂的猜测。我既然试图重复你的问题而无法做到。所以我真的不知道发生了什么。 - Ghetolay
这似乎是通过使用HttpClient(Angular 4.3+)修复的 - Mido


答案:


我发现了这种行为的潜在原因。

谢谢 https://github.com/ghetolay 和 https://github.com/dklmuc 

我们发现angular会取消非常快速的http请求,而不需要任何回调。所以,你必须在订阅中传递onNext

https://github.com/ghetolay

好吧,我认为这是竞争条件

它总是会在拆解时调用xhr.abort()

如果浏览器仍认为连接已打开,它将关闭它,否则可能什么都不做

所以当你对响应进行非常快速的处理时(比如没有回调真的很快),它可能会中止仍然被认为是开放的连接

这个工作正常:

for (let i = 1; i < 50; i++) {
    http.get(`https://swapi.co/api/people/${i}`).subscribe((result) => {
        console.log(i, result);
    });
}

8
2017-08-25 13:21



现在我感觉很糟糕,因为我要对此发表评论,但只是假设你的例子已经简化了。如果您需要在没有订阅的情况下执行http请求。你可以打电话 http.get(...).take(1) 在获取第一个项目后,它将订阅并销毁订阅。 - cgTag
@ctTag是(1)将调用请求事件而不使用subscribe方法? - WimmDeveloper
@ctTag不,它不会。现在检查。但我刚刚意识到,使用http方法非常合理 .take(1) - WimmDeveloper
非常感谢引用我,但这只是一个疯狂的猜测。我既然试图重复你的问题而无法做到。所以我真的不知道发生了什么。 - Ghetolay
这似乎是通过使用HttpClient(Angular 4.3+)修复的 - Mido


这与被观察者被取消有关,虽然我必须承认我不知道你面临的问题的根本原因。

我使用ngrx / effects模块遇到了类似的问题。操作处理程序使用ngrx switch map operator向服务发出请求。除最后一个请求外的所有请求都将被取消将其更改为合并映射修复了问题。 以下是代码

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

在这里复制ngrx的相关部分。

https://www.learnrxjs.io/operators/transformation/switchmap.html

switchMap与其他展平运算符的主要区别在于取消效果。在每次发射时,前一个内部observable(您提供的函数的结果)被取消,并且新的observable被订阅。您可以通过短语切换到新的observable来记住这一点。


4
2018-02-23 11:34





对我来说问题是因为一个空的可观察者。 我在用 Observable.forkJoin 在可观察的列表上。

解: 我需要给我 Observable.of() 一个值: Observable.of(null)


3
2018-03-20 01:03