Skip to content

Latest commit

 

History

History
121 lines (98 loc) · 6.07 KB

File metadata and controls

121 lines (98 loc) · 6.07 KB

Solution

Submission link - https://bigfrontend.dev/problem/create-an-Observable/discuss/373

class Observer {

  constructor(handlers) {
    if (typeof handlers == 'function') { // treat function as next method
      this.handlers = {
        next: handlers
      }
    } else { // else assume that we are getting next, error and complete method
      this.handlers = handlers;
    }
    this.isUnsubscribed = false; // initialize as false for the first time
  }
  next(value) {
    if (!this.isUnsubscribed && this.handlers.next) {
      this.handlers.next(value);
    }
  }
  error(err) {
    if (!this.isUnsubscribed) { // if and only if Observer isn't unsubscribed
      if (this.handlers.error) { // if ''error' is present (as it can be optional) then call 'error' method
        this.handlers.error(err);
      }
      this.unsubscribe(); // unsubscribe once we catch error
    }
  }
  complete() {
    if (!this.isUnsubscribed) {
      if (this.handlers.complete) { // if 'complete' is present (as it can be optional) then call 'complete' method
        this.handlers.complete();
      }
      this.unsubscribe(); // unsubscribe once we don't have anything for next method
    }
  }
  unsubscribe() { // when unsubscribe method is called 
    this.isUnsubscribed = true; // then set isUnsubscribed to true so that any other method can't be called
    if (this._unsubscribe) {
      this._unsubscribe();
    }
  }
}


class Observable {

  constructor(setup) {
    this._setup = setup; // actual logic is in 'setup'
  }

  subscribe(subscriber) {
    const safeObeserver = new Observer(subscriber); // this handles edge cases and provide 'unsubscribe' method
    safeObeserver._unsubscribe = this._setup(safeObeserver);

    return ({ // return 'unsubscribe' method
      unsubscribe() {
        safeObeserver.unsubscribe();
      }
    });

  }
}

Short story

I didn't have any clue about what is Observable and Observer. I took a day to understand it and I'm still not well versed in it.
Though got the basic idea about it.

I might be 200% wrong here so be careful with my assumptions on Event Emitter and Observable.
I think event emitter is a concept where you need to tell when an event is emitted and perform some action that is instructed during the subscription.
In contrast, Observable will trigger a callback when it has something to share.

Example - In event emiter, we use emitter.subscription('event1', callback) and then use emitter('event1) whenever we want to invoke callback.
In Observable, we get callbacks as long as we have a stream of data coming or error or completed.

I know this won't be sufficient to understand, you can connect with me and we both can learn about it together.

Code overview (Might be wrong about few things)

  • We need to create 2 classes viz. Observer and Observable. Though, the developer will pass their own observer but we need to wrap their observer into our own observer to make it safe and add extra functionality on top of it.
  • Say, the developer passes only a single function (instead of 3 methods) then we need to make sure that we treat it as the next method and if they pass only the next and error methods then we call only these 2 methods.
  • Observable is something that we build for developers and which returns the unsubscribe method when subscribed.

Observable

  1. We take the actual logic from the user in an Observable class. This isn't required in the actual library (such as RxJS) because they have methods such as fromEvent, from, fromFetch, etc.
  2. We take that logic and keep it in this._setup variable. The logic is an anonymous function. We will call that function further down in our Observable class.
  3. We add a subscribe method that takes the observer (named subscriber) created by the developer. This is the same observer that has a bunch of callbacks (next, error, and complete).
    1. We pass the subscriber to our Observer class to make it safe and add unsubscribe functionality.
    2. We add the _unsubscribe method to our safeObserver and pass it in this._setup(safeObeserver)
    3. This will invoke the main logic. As mentioned, this._setup has an anonymous function and by using parenthesis we are invoking it.
    4. When we return the subscribe method, which returns an unsubscribe method.
    5. This will call the unsubscribe method of safeObeserver which we set up in 3.ii step. That's all we do in Observable.

Observer

  1. In our case, the Observer has the same 3 methods (sorta Observable convention).
  2. We put some validation logic in these 3 methods and add a unsubscribe method.
  3. We first check if the input is a function or a bunch of methods in an object.
    1. If it is a single function then we wrap that in the next method (that's how a typical Observable works)
    2. If it is an object with 1 or more methods then we direct attach that in our this.handlers.
  4. Let's first dissect the next method.
    1. In the next method, we just check if next method is provided or not along with the unsubscribe flag.
    2. We call the next method only if Observer isn't unsubscribed and next method is provided.
  5. The error and complete method work in the same fashion. They will be called when Observer isn't unsubscribed and error/complete method is provided. They will call unsubscribe method internally to stop next method call in case we hit an error or there is nothing to iterate.
  6. We now have last unsubscribe method. This method will be called by Observerable internally. Refer - 3.iv and 3.v
    1. The unsubscribe method set this.isUnsubscribed flag true and it will call ._unsubscribe method is if it is attached to it.
    2. We attach this through the Observable class. Refer - 3.ii

That's all for this problem.

Resources


I'm excited to improve the solution and code walkthrough. Feel free to drop a comment, or send a PR or send memes @knowkalpesh.