10

I am confused about what the purpose of the "dispose" or "unsubscribe" function is for, which is (optionally) returned from an observable "executor" function, like so:

const Rx = require('rxjs');

const obs = Rx.Observable.create(obs => {

    // we are in the Observable "executor" function
    obs.next(4);

     // we return this function, which gets called if we unsubscribe
    return function () {
        console.log('disposed');
    }

});

    const s1 = obs.subscribe(
        function (v) {
            console.log(v);
        },
        function (e) {
            console.log(e);
        },
        function () {
            console.log('complete');
        }
    );

    const s2 = obs.subscribe(
        function (v) {
            console.log(v);
        },
        function (e) {
            console.log(e);
        },
        function () {
            console.log('complete');
        }
    );


    s1.unsubscribe();
    s2.unsubscribe();

What confuses me is that such a function would actually be more likely to hold on to references in your code and therefore prevent garbage collection.

Can anyone tell me what the purpose is of returning a function in that scenario, what the function is called, and what it's signature is? I am having trouble figuring out information about it.

I also see much more complex examples of returning a subscription from the executor function, for example this:

    let index = 0;

    let obsEnqueue = this.obsEnqueue = new Rx.Subject();

    this.queueStream = Rx.Observable.create(obs => {

        const push = Rx.Subscriber.create(v => {
            if ((index % obsEnqueue.observers.length) === obsEnqueue.observers.indexOf(push)) {
                obs.next(v);
            }
        });

        return obsEnqueue.subscribe(push);
    });

This seems to return a subscription instead of just a plain function. Can anyone explain what's going on with this?

To make it a clear question, what is the difference between doing this:

const sub = new Rx.Subject();

const obs = Rx.Observable.create($obs => {

    $obs.next(4);
    return sub.subscribe($obs);

});

and not returning the result of the subscribe call:

const sub = new Rx.Subject();

const obs = Rx.Observable.create($obs => {

    $obs.next(4);
    sub.subscribe($obs);

});
Mark van Straten
  • 9,287
  • 3
  • 38
  • 57
  • I am not sure if it's actually called an "executor" function, but that's the name of the analogue in the Promises world –  Jan 06 '17 at 06:10

1 Answers1

5

The unsubscribe function that Rx.Observable.create needs to return is invoked when downstream does not listen to the stream anymore, effectively giving you time to clean up resources.

In regards to your question; .subscribe() returns the subscription on which you can call .unsubscribe(). So if you want to do something with an other subscription you can pipe through that subscription to your downstream:

    const obs = Rx.Observable.create($obs => {
      const timer = Rx.Observable.interval(300)
        .do(i => console.log('emission: ' + i))

      return timer.subscribe($obs);
    });
    obs.take(4).subscribe(i => console.log('outer-emission:'+i))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.2/Rx.js"></script>

Without the unsubscribe function you would stop listening to the observable but the interval created internally would keep on running:

const obs = Rx.Observable.create($obs => {
  const timer = Rx.Observable.interval(300)
    .do(i => console.log('emission: ' + i))
    .take(10)
    .subscribe(
      val => $obs.next(val),
      err => $obs.error(err),
      () => $obs.complete()
    );

  return function(){} // empty unsubscribe function, internal subscription will keep on running
});
obs.take(4).subscribe(i => console.log('outer-emission:'+i))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.2/Rx.js"></script>
Mark van Straten
  • 9,287
  • 3
  • 38
  • 57
  • Hi Mark, thanks! In your first code block, if I don't return timer, but leave everything the same, then things work as expected (there seems to be no difference whether I return timer.do or just call timer.do, so not sure if your explanation is 100% correct? It seems that not returning the timer.do call is ok (in the first example)? Regarding second example, I do see that emission keeps get logged even after 4 takes. –  Jan 06 '17 at 08:21
  • But in the first example if we don't return timer.do things still work! I don't get it. –  Jan 06 '17 at 08:27
  • 1
    Hi Olegzandr, in the first example i have chained the operators. So it is not returning the timer but the subscribe() on the timer. I have updated the example to make this more clear. Without returning the subscribe is effectively the same as the second example. – Mark van Straten Jan 06 '17 at 08:45