Infinite loop when catching error from BehaviorSubject subscribed by async pipe on Angular 2

后端 未结 1 578
北恋
北恋 2021-01-26 05:35

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

相关标签:
1条回答
  • 2021-01-26 06:11

    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
    

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

    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 Subjects that you want to emit data throughout the complete lifetime of the application.

    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.

    The lifecycle would look something like this:

    Generative Flow

    1. Some Event (e.g. a Button-Click, or some Timebased Event)
    2. Service.generateOrFetchData().handleErrors()
    3. StoreService.someSubj.next(data) - (step 3 could be optional, depending on how you want to handle errors in step 2)

    Subscribing Flow

    1. UI Subscribes to StoreService.someSubj
    2. UI automatically updates whenever new data is ejected, no error-handling required

    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.

    0 讨论(0)
提交回复
热议问题