11

I have these two objects, and I want to stop listening to their events. I am totally new to observables and RxJS and just trying to work with the Inquirer library.

Here is the RxJS API for reference: http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html

How can I unsubscribe from these types of observables?

ConnectableObservable:

   ConnectableObservable {
     source: EventPatternObservable { _add: [Function], _del: [Function], _fn: undefined },
     _connection: ConnectDisposable { _p: [Circular], _s: [Object] },
     _source: AnonymousObservable { source: [Object], __subscribe: [Function: subscribe] },
     _subject: 
      Subject {
        isDisposed: false,
        isStopped: false,
        observers: [Object],
        hasError: false } },
  _count: 1,
  _connectableSubscription: 
   ConnectDisposable {
     _p: 
      ConnectableObservable {
        source: [Object],
        _connection: [Circular],
        _source: [Object],
        _subject: [Object] },
     _s: AutoDetachObserver { isStopped: false, observer: [Object], m: [Object] } } }

FilterObservable:

FilterObservable {
  source: 
   RefCountObservable {
     source: 
      ConnectableObservable {
        source: [Object],
        _connection: [Object],
        _source: [Object],
        _subject: [Object] },
     _count: 1,
     _connectableSubscription: ConnectDisposable { _p: [Object], _s: [Object] } },
  predicate: [Function] }

I need to unsubscribe from these objects:

'use strict';
var rx = require('rx');

function normalizeKeypressEvents(value, key) {
  return {value: value, key: key || {}};
}

module.exports = function (rl) {

  var keypress = rx.Observable.fromEvent(rl.input, 'keypress', normalizeKeypressEvents)
    .filter(function (e) {
      // Ignore `enter` key. On the readline, we only care about the `line` event.
      return e.key.name !== 'enter' && e.key.name !== 'return';
    });

  return {
    line: rx.Observable.fromEvent(rl, 'line'),

    keypress: keypress,

    normalizedLeftKey: keypress.filter(function (e) {
      return e.key.name === 'left';
    }).share(),

    normalizedRightKey: keypress.filter(function (e) {
      return e.key.name === 'right';
    }).share(),

    normalizedUpKey: keypress.filter(function (e) {
      return e.key.name === 'up' || e.key.name === 'k' || (e.key.name === 'p' && e.key.ctrl);
    }).share(),

    normalizedDownKey: keypress.filter(function (e) {
      return e.key.name === 'down' || e.key.name === 'j' || (e.key.name === 'n' && e.key.ctrl);
    }).share(),

    numberKey: keypress.filter(function (e) {
      return e.value && '123456789'.indexOf(e.value) >= 0;
    }).map(function (e) {
      return Number(e.value);
    }).share(),

    spaceKey: keypress.filter(function (e) {
      return e.key && e.key.name === 'space';
    }).share(),

    aKey: keypress.filter(function (e) {
      return e.key && e.key.name === 'a';
    }).share(),

    iKey: keypress.filter(function (e) {
      return e.key && e.key.name === 'i';
    }).share()
  };
};

My current best guess is that no explicit call to subscribe is happening like this:

var source = Rx.Observable.fromEvent(input, 'click');

var subscription = source.subscribe(
  function (x) {
    console.log('Next: Clicked!');
  },
  function (err) {
    console.log('Error: %s', err);
  },
  function () {
    console.log('Completed');
  });

but instead, there are these calls:

events.normalizedUpKey.takeUntil(validation.success).forEach(this.onUpKey.bind(this));
events.normalizedDownKey.takeUntil(validation.success).forEach(this.onDownKey.bind(this));

so my best guess is that I need a way to nullify/cancel the takeUntil call.

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817

3 Answers3

22

If you want to unsubscribe you need to have the Subscription object. That's the object returned from every Observable.subscribe() call. For example:

let subscriber = Observable.subscribe(...);
...
subscriber.unsubscribe();

For more info see: https://github.com/ReactiveX/rxjs/blob/master/doc/subscription.md

martin
  • 93,354
  • 25
  • 191
  • 226
  • yeah, can you tell if that call is happening internally in any of the above code? it's pretty obviously that the code I am working with is effectively subscribing to observable events, I just don't see any explicit subscribe() calls in the code... – Alexander Mills Nov 12 '16 at 08:43
  • 1
    for example, it looks like the rx.Observable.fromEvent() is doing the work here, but I don't know how stop it! – Alexander Mills Nov 12 '16 at 08:44
  • this helps a bit => https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/fromevent.md – Alexander Mills Nov 12 '16 at 08:45
  • I think to get an accepted answer you have to help me undo the takeUntil call :) – Alexander Mills Nov 12 '16 at 08:59
3

I second what the first commenter was saying. However it feels that there needs to be a call like this somewhere in the code:

let subscription = normalizedUpKey.subscribe( data => console.log('data') );

that you could do

subscription.unsubscribe()

on. How else would you know when something happens, or is that part of the 3rd party library?

To read more about Rxjs. I suggest you have a look at this free book, https://www.gitbook.com/book/chrisnoring/rxjs-5-ultimate/details

Chris Noring
  • 471
  • 3
  • 9
1

There is no need and no protocol for unsubscribing from an observable. Actually, I see the code in your question, especially the part of the return object where a bunch of observables composed by share was included. However, these observables are still observables, rather than subscription, which means that there is no such concept called unsubscribing for these elements.

Therefore, if you have some new subscribe-like codes outside the module and exactly work with the events observables, you can obviously unsubscribe the specific subscription instance.

Currently, for the code in the question, the methods used on the source observable are all operators, such as .filter() .share() .takeUntil(), rather than subscribing execution, which are actually the methods return new observables.

As described in the Rxjs official document, although .share() creates multicasted observables, it is still possible that the execution will stop if the number of subscribers decreases from 1 to 0 when using some convenient operators, where the .share() in your code is exactly included as well.

In conclusion, there is no need to worry about the event unsubscribing for your code in your question. Potentially, there is really a further problem which sounds like the concern described in your question: If you use .connect() instead of .share(). It is the situation about ConnectableObservable where you need to concern about manually canceling event binding.

千木郷
  • 1,595
  • 2
  • 19
  • 30