JSFiddle - React, Tailwind, and code Playground

HTML

<div id="console"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.0/Rx.js"></script>

TypeScript

/////////// Helper function to print to JsFiddle output window //////////////////////////
let console = document.getElementById('console');
let log = (text) => {
  console.innerHTML = console.innerHTML + text + '</br>';
}
/////////////////////////////////////////////////////////////////////////////////////////

class SvcA {
  private sub = new Rx.Subject();
  private synced = false;

  poke() {
  	this.synced = false;
    log('service A is doing some work');
    this.sub.next();
  }
  
  sync() {
  	if (!this.synced) {
    	log('syncing service A');
    	this.synced = true;
      this.sub.next();
    }
  }

  events() {
    return this.sub.asObservable();
  }
}

class SvcB {
  private sub = new Rx.Subject();
  private synced = false;

  poke() {
  	this.synced = false;
    log('service B is doing some work');
    this.sub.next();
  }
  
  sync() {
  	if (!this.synced) {
    	log('syncing service B');
    	this.synced = true;
      this.sub.next();
    }
  }

  events() {
    return this.sub.asObservable();
  }
}

// WARNING! The code in the ObserverY class throws the following error in the 
// browser console: "Uncaught RangeError: Maximum call stack size exceeded".
class ObserverY {

  constructor(svcA, svcB) {
    svcA.events().subscribe(() => {
      svcB.sync();
    });

    svcB.events().subscribe(() => {
      svcA.sync();
    });
  }
}

class WidgetComponent {
  constructor(svcA) {}
  
  click() {
  	svcA.poke()
  }
}

let svcA = new SvcA();
let svcB = new SvcB();
new ObserverY(svcA, svcB);

let widget = new WidgetComponent(svcA);
widget.click();