简体   繁体   中英

Is an Observer a 'listener' to an Observable in RxJS?

I'm learning RxJS and am rather confused as to where the "listeners" are (in the Observable or the Observer), how they are subscribed/unsubscribed, and what happens when an Observer is "no longer interested in" an Observable, such as when you use take or takeUntil .

For the first part -- what's subscribed to what, what's a listener -- I'm confused by the seeming contradiction between these statements. From http://reactivex.io/rxjs/manual/overview.html we read that Observers are not 'listeners' to Observables

This is drastically different to event handler APIs like addEventListener / removeEventListener. With observable.subscribe, the given Observer is not registered as a listener in the Observable. The Observable does not even maintain a list of attached Observers.

but in http://reactivex.io/learnrx/ it says (Exercise 30) (highlighting mine) that

An Observable based on an Event will never complete on its own. The take() function creates a new sequence that completes after a discrete number of items arrive. This is important, because unlike an Event, when an Observable sequence completes it unsubscribes all of its listeners . That means that if we use take() to complete our Event sequence, we don't need to unsubscribe!

This seems contradictory to me. When you set up an Observable with, for example, fromEvent , where is the event listener? When you use take(1) , for instance, on an Observable based on DOM events, what happens after the first event is sent to the observer? Does the Observer unsubscribe from the Observable, which continues to emit events, it's just that the Observer isn't listening to them anymore? Or does the Observable somehow unsubscribe the Observer, that is, the eventListener was in the Observable, not the Observer?

Thanks for any clues -- obviously I'm not seeing the forest for the trees, but the tutorials I'm working through, while they are good at trying to explain it conceptually, leave me confused as to what's actually going on.

The first part is being rather particular about its use of words in order to highlight that subscribing to an observable is a matter of calling a function (or more likely a chain of functions) to run all the code they contain. The second part is less particular about its wording, but it's not really talking about the same thing. If you like, the second part would be better worded as "when an observable completes, it calls teardown logic on its observers.

Let me try to describe what i mean when i say that subscribing to an observable is a matter of calling a chain of functions. Consider the following super simple example:

For a super simple example, suppose i create this observable:

const justOne = Rx.Observable.create(function realSubscribe(observer) {
  observer.next(1);
  observer.complete();
});

justOne.subscribe(val => console.log(val));

If i then call justOne.subscribe(val => console.log(val)) , doing so will immediately call the function i named realSubscribe. It then does observer.next(1) , which results in logging out val, then it does observer.complete() . And that's it.

No where in this process did the observable create or augment a list of subscribers; it just ran through the code sequentially and then was done.


Now moving onto a slightly more realistic example, let's consider fromEvent . If i were to implement it, it might look something like this (the real implementation is more complicated, but this gets the gist of it):

function fromEvent(element, eventName) {
  return Rx.Observable.create(function subscribeToEvent(observer) {
    element.addEventListener(eventName, observer.next);
    return function cleanup() {
      element.removeEventListener(eventName, observer.next);
    }
  });
}

const observable = fromEvent(document, 'click');
const subscription = observable.subscribe(event => console.log(event));

Now when i call observable.subscribe, it runs subscribeToEvent, and in so doing it calls addEventListener on the document. document.addEventListener does result in the document keeping a list of event listeners, but that's because of the way addEventListener is implemented, not something common to all observables. The observable itself doesn't keep track of any listeners. It just calls what it's told to call, and then returns a cleanup function.


Next up let's look at take. As before the real implementation is more complicated, but here's roughly what it does:

// In the real `take`, you don't need to pass in another observable since that's
// available automatically from the context you called it in. But my sample code
// has to get it somehow.
function take(count, otherObservable) {
  return new Observable(function subscribeToTake(observer) {
    let soFar = 0;
    otherObservable.subscribe((value) => {
      observer.next(value);
      soFar++;
      if (soFar >= count) {
        observer.complete();
      }
    });
  });
}

const clickObservable = fromEvent(document, 'click');
take(1, clickObservable).subscribe(event => console.log(event))

As mentioned in the comment, the syntax i'm using doesn't quite match how it would be use in rxjs, but that's because to mimic that would require a more full implementation. Anyway, the main thing to draw your attention to is that we're starting to produce a chain of functions:

When i call .subscribe , that calls subscribeToTake. This sets up a counter, and then calls otherObservable.subscribe, which is subscribeToEvent. subscribeToEvent then calls document.addEventListener.

Take's job is to sit in the middle of this function chain. It keeps track of how many values have been emitted so far. If the count is low enough, it just forwards the values along. But once the count is reached, it will call complete, thus ending the observable. Calling complete causes the observable to run any teardown logic it has, or anything its chain has. There's no teardown logic for take , but fromEvent will run some teardown logic to remove the event listener.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM