Search code examples
javascriptnode.jsbrowserifyrxjseventemitter

Is this really the best way to get a Rx.Observable from an event callback?


I want to get a stream of events from datamaps library but since that library is not really written with Reactive Programming in mind I'm having a difficult time to map any emitted event from it into an Observable. My best attempt so far is this, but it doesn't feel so idiomatic. Is this the best I can do or am I maybe missing some basic RxJs construct that would make this better/simpler/more readable?

import Rx from 'rx';
import {EventEmitter} from 'events';
import Datamap from 'datamaps';

const eventEmitter = new EventEmitter();
const mapEvents$ = Rx.Observable.fromEvent(
  eventEmitter,
  'mapEvent'
)
const map = new Datamap({
  element: document.getElementById('map'),
  done: (datamap) => {
    datamap.svg
      .selectAll('.datamaps-subunit')
      .on('mouseover', (o) => {
        eventEmitter.emit('mapEvent', o);
      });
  }
});


Solution

  • Looks like you're using an EventEmitter as an event bus, but we already have Subjects you could use instead. Subjects are both observers and observables, which means you can call onNext on them to give them the next event and you can use operators on the observable sequence.

    Simply, I'd rewrite it to something like this:

    import Rx from 'rx';
    import Datamap from 'datamaps';
    
    const datamapsSubject = new Rx.Subject();
    const map = new Datamap({
      element: document.getElementById('map'),
      done: (datamap) => {
        datamap.svg
          .selectAll('.datamaps-subunit')
          .on('mouseover', (o) => {
            datamapsSubject.onNext(o);
          });
      }
    });
    

    If you need something you can construct on subscription and destroy on disposal/unsubscription, you might also consider Observable.create, but it looks like you're only targeting that one div element for now.