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();
}
});
}
}
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.
- 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 thenext
anderror
methods then we call only these 2 methods. - Observable is something that we build for developers and which returns the unsubscribe method when subscribed.
- 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 asfromEvent
,from
,fromFetch
, etc. - 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. - We add a
subscribe
method that takes the observer (namedsubscriber
) created by the developer. This is the same observer that has a bunch of callbacks (next, error, and complete).- We pass the
subscriber
to ourObserver
class to make it safe and add unsubscribe functionality. - We add the
_unsubscribe
method to oursafeObserver
and pass it inthis._setup(safeObeserver)
- This will invoke the main logic. As mentioned, this._setup has an anonymous function and by using parenthesis we are invoking it.
- When we return the subscribe method, which returns an unsubscribe method.
- This will call the unsubscribe method of
safeObeserver
which we set up in 3.ii step. That's all we do in Observable.
- We pass the
- In our case, the
Observer
has the same 3 methods (sorta Observable convention). - We put some validation logic in these 3 methods and add a unsubscribe method.
- We first check if the input is a function or a bunch of methods in an object.
- If it is a single function then we wrap that in the
next
method (that's how a typical Observable works) - If it is an object with 1 or more methods then we direct attach that in our
this.handlers
.
- If it is a single function then we wrap that in the
- Let's first dissect the
next
method.- In the
next
method, we just check if next method is provided or not along with the unsubscribe flag. - We call the
next
method only ifObserver
isn't unsubscribed and next method is provided.
- In the
- 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 stopnext
method call in case we hit an error or there is nothing to iterate. - We now have last
unsubscribe
method. This method will be called byObserverable
internally. Refer - 3.iv and 3.v- The
unsubscribe
method setthis.isUnsubscribed
flag true and it will call._unsubscribe
method is if it is attached to it. - We attach this through the
Observable
class. Refer - 3.ii
- The
That's all for this problem.
I'm excited to improve the solution and code walkthrough. Feel free to drop a comment, or send a PR or send memes @knowkalpesh.