Home > database >  Is it ok to have a side effect event emission in RxJS pipe?
Is it ok to have a side effect event emission in RxJS pipe?

Time:01-20

I am using RxJS in context of angular application.

I have a service that allows to renitialize whole application using different settings on demand

@Injectable()
class BootstrapService{

 public initApplication(config:appConfig):Observable<boolean>{
        return somehowCreatedObservable;
 };
}

the intended usage is as follows

{
 
///.... code in part of application that allows user to reinit app

this.bootstrapService.initApplication({someconfig}).subscribe(result=>{
  if(result){
      //report success
   }else report failure
}
}

so this acts as cold observable just like in http calls.

Now, I have a need to send additional notification to some concerned components (for example to rebuild user menus, toolbars etc - to reflect new configuration loaded from the server).

I want to do this by adding additional event emission using subject at the end of pipe inside BoostrapService#initApplicaiton, like this

 public initApplication(config:appConfig):Observable<boolean>{
       return somehowCreatedObservable().pipe(tap(result=>if(result)this.subject.next(someEvent))
 };

This is however a side effect (if I am not mistaken), while functional programming should not avoid such constructs as far as I understand what can be found over the internet regarding this topic.

Then the question is, is it ok to emit such events as a side effect, or it should be solved somehow differently?

Another solution that comes to my mind but still is somehow not right is to make my action a "hot" observable that is not returned to the caller, but instead common stream would be used eg

 appInitResult:Subject<boolean>
 public initApplication(config:appConfig):Observable<boolean>{
      somehowCreatedObservable().subscribe(r->this.appInitResult.next(r));
  return this.appInitResult.asObservable();
 };

so everything can subscribe to exactly same stream including method caller.

CodePudding user response:

While external side effects should be avoided, they are sometimes inevitable. And the way you are implementing this side effect is correct.

That being said, my first question would be what scenario requires you to reinitialize the app?

In the example case of a logged in user, you can setup your app's reactive data flow where the user observable is at the top of that flow. That way, whenever the user variable emits a new user, all observables will react to that value and emit their own updated values based on the user object.

Setting this up can be a bit daunting, as it requires you to use observables everywhere.

CodePudding user response:

It looks like you are using Angular, in which case let me try to give you my opinion.

If I understand right, you have an app where many components are interested in the same stream of events.

If this is the case, what I would do is create a myService which exposes 2 APIs:

  • a public Observable which notifies the event the components are interested in
  • a public method that allow an external client of the myService to trigger the notification of the event

In other words the code would be

@Injectable()
class MyService{
 private _myEvent = new Subject<any>()
 public myEvent = this._myEvent.asObservable()

 public notifyEvent(event: any) {
   this._myEvent.next(event)
 };
}

Now whichever component or service wants to use myService can get it via Dependency Injection and use it.

For instance BootstrapService would be something like this

@Injectable() class BootstrapService{ constructor(private myService: MyService) {}

public initApplication(config:appConfig) { // do whatever is needed to create the event const _event = buildEvent(config) this.myService.notifyEvent(event) }; }

Any other component that needs to be notified will be like this

@Injectable()
class MyComponent{
 constructor(private myService: MyService) {}
 
 // use myService.myEvent as required, e.g. subscribing to it in ngOnInit
 public ngOnInit() { 
        const _event = buildEvent(config)
        this.myService.myEvent.subscribe({
          next: event => {
             // do whatever with the event
          }
        })
 };
}

Components built like this can also use myService.myEvent directly in the template with the async pipe, which is the suggested approach if the only things to do are to display stuff contained in the event.

You may find this article interesting. It talks about React, but the concept of the service is the same as the one I tried to describe above.

Last, regarding your comment "functional programming should avoid side effects", I think it has to be considered with a pinch of salt.

Functional programming can not avoid side effects. Any program must have side effects if it wants to do something useful e.g. write something or show something. What is important is to isolate side effects, and this is where functional programming can help: it helps isolate side effect.

In this case side effects are isolated in the subscribe logic.

  •  Tags:  
  • Related