2
votes

Consider the following scenario:

observerX => observableA <= observerY => observableB <= observerZ
  • There are two Observables (A and B) and three Observers (X, Y, and Z).
  • Each one of those Observers and Observables are within separate (Angular) services.
  • Observer Y observes both Observable A and Observable B, which stay "alive" over the life of the application, i.e. they do not complete.
  • When Observable A triggers an event, Observer Y calls a function on the service of Observable B, which causes Observable B to trigger an event. Similarly, when Observable B triggers an event, Observer Y calls a function on the service of Observable A, which in turn causes Observable A to trigger an event.

Ex: Observer Y...

svcA.obsA.subscribe(()=> {
    svcB.doSomething()
});

svcB.obsB.subscribe(()=> {
    svcA.doSomething()
});

As you can see, all Observer Y is trying to do is to keep service A and service B in sync.

The problem: The setup as described above causes an infinite execution loop. One idea that came to my mind to get around this issue is to set a flag in each subscribe, and when that flag is set the other subscribe will ignore the next event. That is not a proper solution however because the next event may not necessarily be the one that corresponds to Observer Y calling doSomething(), but may be due to Observer X or Z calling doSomething().

Is there an elegant solution to this use-case? Do I have an architectural issue here? Please let me know if my description of the problem is not clear.

JSFiddles:

2
Can you specify problem more concretely? I.e. what are you doing the the data from Observable A and B? What kind of data are we taking about, and what is the relationship forcing you to sync two Observables? Maybe you could use jorkJoin? - Siegmeyer
@Siegmeyer thank you for your response. I will need to get back to you with more details in a couple of days when I'm back at work. - Sam Herrmann
@Siegmeyer I have added JSFiddle examples that will hopefully help clarify the problem. You will see that the observables do not return any data, they simply notify the observers that a task was completed. The reason for this setup between service A and service B is to keep them modular and completely decoupled from each other. It is the responsibility of ObserverY (application logic) to make sure that when one of the services is poked to perform their task that the other service does its part as well. I hope this helps. - Sam Herrmann

2 Answers

1
votes

I think you can use thottletime operator (https://www.learnrxjs.io/learn-rxjs/operators/filtering/throttletime) with a short duration selector e.g, 100ms for both observable A and B.

obsA.pipe(throttleTime(100)).subscribe(val => obsB.next(val));
obsB.pipe(throttleTime(100)).subscribe(val => obsA.next(val));

Asumming that the second emits of observables A and B are less than 100ms,they will be ignored.

0
votes

Okay, so this relation can be modeled using following automaton (ObserverY only forwards so it can be ignored):

enter image description here

It will loop forever. The problem is that you can't avoid infinite loop using only one channel (i.e. without checking if message is from outside or neighbor (A/B)).

So, your options:

  1. Couple objects together, so they can differentiate between messages (A/B can tell if message is from B/A).

  2. Use separate channels for passing information (from what I know you can't to this with Rx, but I may be wrong).

  3. Use this hack: https://jsfiddle.net/6oz6mgLL/, which adds separate method sync() and bool synced. Objects are not coupled, but each outside event will cost you 1 duplicated method call (study code to see why).

  4. This doesn't follow from your problem description, but if use ReplaySubject which holds only one item and broadcasts only if it changed, solution in 3. will work smoothly without extra calls. I don't know if you can apply ReplaySubject though.

If objects are coupled on the conceptual level, keeping the decoupled in the code may not be always possible.

Hoped this helped at least a bit.