Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RxJS detect when observable has been subscribed to

I have a need to detect when an observable (observedEvents) has been subscribed to, and then subscribe to another observable (triggerEvent). I don't want to subscribe to triggerEvent manually, but only once and when observedEvents has a subscription.

Here is some code explaining what I am looking for:

// This just emits events
let emitting = new EventEmitter();

// This is the main Observable which someone else might
// have access to
let observedEvents = Rx.Observable.merge(
  Rx.Observable.fromEvent(emitting, 'aba'),
  Rx.Observable.fromEvent(emitting, 'bob')
)

// This trigger should get a subscription if observedEvents
// has one, i.e. when I subscribe to observedEvents
// that subscription activates this trigger

// I have made an attempt at this by calling skipUntil
// this however skips one event, but I don't want that
let triggerEvent = Rx.Observable.merge(
  // these actions are things that can
  // happen when the trigger is active
  Rx.Observable.of('a').delay(200),
  Rx.Observable.of('b').delay(400),
  Rx.Observable.of('c').delay(600)
)
.skipUntil(observedEvents);

// Something else should be used to activate trigger
// I don't want to do this part manually
triggerEvent.subscribe(val => {
    console.log(`Do something fancy with ${val}`);
});

//----------------------------------------------------
// Somewhere else in the code...
//----------------------------------------------------
observedEvents.subscribe(evt => {
  console.log(`Some event: ${evt}`);
});
// At this point I want triggerEvent to become active
// because observedEvents has a subscription

setTimeout(() => {
  emitting.emit('bob', 'world');
  setTimeout(() => emitting.emit('aba', 'stackoverflow!'), 500);
}, 200);
<!DOCTYPE html>
<html>
<head>
  <script src="https://npmcdn.com/@reactivex/[email protected]/dist/global/Rx.umd.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/EventEmitter/5.1.0/EventEmitter.min.js"></script>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>

</body>
</html>

Is this possible?

I hope that explains what I'm looking for.

As I'm writing this, I'm thinking a solution with Subjects is probably what I need. I'm not sure, but I just need a nudge in the right direction or a solution if possible.

like image 721
smac89 Avatar asked Jun 14 '17 22:06

smac89


2 Answers

Sure enough I was right about using Subjects. The key was the observers list for Subject. Here is what I finally did:

let emitting = new EventEmitter();
let sub = new Rx.Subject();

// return this to users
let myGlobalSub = sub.merge(Rx.Observable.of(1, 2, 3));

// For internal use
let myObservers = Rx.Observable.fromEvent(emitting, 'evt');

console.log(`The number of subscribers is ${sub.observers.length}`);

// Only do something if myGlobalSub has subscribers
myObservers.subscribe(l => {
  if (sub.observers.length) { // here we check observers
    console.log(l);
  }
});

// Somewhere in the code...
emitting.emit('evt', "I don't want to see this"); // No output because no subscribers

myGlobalSub.subscribe(l => console.log(l)); // One sub

emitting.emit('evt', 'I want to see this'); // Output because of one sub

console.log(`The number of subscribers is ${sub.observers.length}`);
<!DOCTYPE html>
<html lang=en>

<head>
  <script src="https://unpkg.com/[email protected]/bundles/Rx.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/EventEmitter/5.2.5/EventEmitter.min.js"></script>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>

<body>
</body>

</html>
like image 114
smac89 Avatar answered Sep 22 '22 13:09

smac89


The answer by @smac89 use the observersproperty which has been deprecated in RxJS 7 and is scheduled to be removed in version 8.

If you only want to know if an Observable has been subscribed to, you may use the observed boolean property instead.

There is currently no real equivalent to the observers array.

let emitting = new EventEmitter();
let sub = new rxjs.Subject();

// return this to users
let myGlobalSub = rxjs.merge(sub,rxjs.of(1, 2, 3));

// For internal use
let myObservers = rxjs.fromEvent(emitting, 'evt');

console.log(`Has the subject been subscribed to ? ${sub.observed}`);

// Only do something if myGlobalSub has subscribers
myObservers.subscribe(l => {
  if (sub.observed) { // here we check observers
    console.log(l);
  }
});

// Somewhere in the code...
emitting.emit('evt', "I don't want to see this"); // No output because no subscribers

myGlobalSub.subscribe(l => console.log(l)); // One sub

emitting.emit('evt', 'I want to see this'); // Output because of one sub

console.log(`Has the subject been subscribed to ? ${sub.observed}`);
<!DOCTYPE html>
<html lang=en>

<head>
  <script src="https://unpkg.com/rxjs@%5E7/dist/bundles/rxjs.umd.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/EventEmitter/5.2.5/EventEmitter.min.js"></script>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>

<body>
</body>

</html>
like image 31
Julien Avatar answered Sep 26 '22 13:09

Julien