从Angular 2上的异步管道订阅的BehaviorSubject捕获错误时发生无限循环 [英] Infinite loop when catching error from BehaviorSubject subscribed by async pipe on Angular 2
问题描述
我有一个rxjs BehaviorSubject
,我使用来自Angular 2的async
管道订阅,我有一个catch
来处理它引发的最终错误.
问题是,每次遇到错误时,它都会启动一个无限循环,因为我的捕获返回了从BehaviorSubject
派生的Observable,并且我认为当我返回catch
时async
管道将重新订阅到observable.
I have an rxjs BehaviorSubject
I subscribe to using an async
pipe from Angular 2 and I have a catch
to handle eventual errors it throws.
Problem is, every time I get an error it starts an infinite loop because my catch returns the Observable derived from the BehaviorSubject
and I think the async
pipe resubscribes to the observable when I return the catch
.
代码大致如下:
ListService -是@Injectable
,其中具有BehaviorSubject和具有Observable
的属性.
ListService - is a @Injectable
where I have the BehaviorSubject and the property with the Observable
.
private listSubject: BehaviorSubject<IItem[]>;
public get listObservable() {
return this.listSubject.asObservable()
}
private error(error: IError) {
this.listSubject.error(error);
}
ListComponent -是@Component
,显示可观察的列表.
ListComponent - is a @Component
that shows the list observable.
// Template
<items-view [items]="list | async"></items-view>
// Code
public get list() {
return this.listService.listObservable
.catch((error) => {
this.handleError(error);
return this.listService.listObservable;
});
}
如您所见,我的catch返回当前可观察值,因为它必须返回一个可观察值.因此,发生的是,当我发送this.listSubject.error(error)
时,代码进入了无限循环调用catch
的无限循环,因为像我之前说的那样,我认为BehaviourSubject
重新抛出错误是因为async
管道当catch
返回它时,重新订阅该可观察对象.
As you can see, my catch returns the current observable, as it MUST return an observable. So, what happens is, when I send the this.listSubject.error(error)
the code enters an infinite loop calling the catch
indefinitely because, like I said before, I think that the BehaviourSubject
re-throws the error because the async
pipe re-subscribes to the observable when the catch
returns it.
我试图返回错误中的上一个缓存数组以返回Observable.of(error.cached)
,但由于遇到了异步不再订阅BehaviorSubject
的问题,我遇到了一系列全新的问题.
I tried to return my previous cached array in the error to return an Observable.of(error.cached)
, but I got a whole new set of problems because think the async wasn't subscribed to the BehaviorSubject
anymore.
就像我之前说过的那样,这是我的真实代码的粗略表示,但是逻辑基本上就是这样.
Like I said before, this is a rough representation of my real code, but the logic is basically that.
我一直在尝试各种不同的方法,但是我无法使这个无限循环停止.
I have been trying various different approaches to this but I couldn't manage to get this infinite loop to stop.
预先感谢您的帮助.
推荐答案
通常,在主题上手动分发错误通常是个坏主意,该主题应该仅弹出数据(例如您的情况下的BehaviorSubject
).原因是,当Subject
上发生错误时,Subject
本质上是 dead ->含义,不再有新数据弹出.这是rxjs的核心概念之一.这是一个小例子:
It is a generally bad idea to manually dispatch an error on a Subject, that is supposed to only eject data (like the BehaviorSubject
in your case). The reason is, that when an error occurs on a Subject
, the Subject
is essentially dead -> meaning, that no new data can be ejected on it any more. This is one of the core-concepts of rxjs. Here is a small example:
let stream$ = new Rx.BehaviorSubject(1);
stream$.subscribe(x => console.log("Logging: " + x), e => console.error("Error: " + e)); // logs 1, 2, Error: ...
stream$.next(2);
stream$.error(new Error("Some error message."));
stream$.next(3); // this will have no effect, because the stream is "dead"
stream$.subscribe(x => console.log("Logging 2: " + x), e => console.error("Error: " + e)); // this will just log the error
对于您的情况,这意味着您进行了一些错误处理,但是只返回了旧的,死的,错误"-主题-换句话说:将错误向下传播. (我不认为handleError()
创建一个新的Subject
,无论如何这都是可怕的做法.)
For your case this means that you did some error-handling, but then just return the "old, dead, error"-Subject - in other words: propagate the error down the stream. (I'm not assuming that handleError()
creates a fresh Subject
, which would be an awful practice anyways.)
通常,这意味着.error
仅应用于执行定义的操作并具有确定数量的结果,然后完成或引发错误的流,而不能用于要发射数据的Subject
在整个应用程序的整个生命周期中.
In general it means that .error
should only be used for streams that perform a defined operation and have a defined number of results and then complete or throw an error, but not on Subject
s that you want to emit data throughout the complete lifetime of the application.
如何根据您的情况解决此问题:快速&肮脏(真的很脏!)方法是使用两个单独的主题,一个用于数据,另一个用于错误(用.next
弹出).
How to solve this in your case: The quick&dirty (really dirty!!) way would be to use two separate Subjects, one for the data and one for errors (eject with .next
).
正确的解决方法:将体系结构分为数据生成流和数据存储部分.
The proper fix: Split up your architecture into a data-generation-flow and into a data-store-part.
生命周期如下:
生成流
- 某些事件(例如,单击按钮或某些基于时间的事件)
- Service.generateOrFetchData().handleErrors()
- StoreService.someSubj.next(data)-(步骤3可以是可选的,具体取决于您要如何处理步骤2中的错误)
订阅流程
- UI订阅StoreService.someSubj
- 每当弹出新数据时,UI都会自动更新,无需进行错误处理
完美的修复方法是使用易于使用的,经过深思熟虑的商店架构,例如
The perfect fix would be to use a ready-to-use thought-through store-architecture like ngrx, however implementing this in an existing project will come with major refactoring requirements.
这篇关于从Angular 2上的异步管道订阅的BehaviorSubject捕获错误时发生无限循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!