TL;DR
No.
Goodbye State đź‘‹
In our previous post we
defined observers as objects that take a subject and can pull the
state in.
const createObserver = subject => {const observer = {subject,notify() {console.log("State updated to", this.subject.state)}}subject.observers.push(observer)return observer}const observer1 = createObserver(subject)
With Observables, the concept of “state” is gone. We’re now thinking
in events (AKA “pushing values over time”).
Because state is gone, we can rip most everything out of our observer
and leave it with a single callback. Compare this snippet below to the much
longer snipped above and you’ll see the subject is completely gone.
const observer = {notify(value){ //notify is named `next` in RxJSconsole.log(`new value received`, value)}}
With such a simple observer, this leaves all the remaining work
to be done from the source object (AKA the Observable).
So let’s create a factory for our Observable that takes in an observer:
const createObservable = observer => {observer.notify({ message: "hello" })}
Now, we could push as many values into observer.notify as we needed. In fact,
you could set up an event listener inside the createObservable and
you’re mostly following the spirit of Observables
//The spirit is there, but it's still missing important piecesconst createObservable = observer => {document.addEventListener("click", observer.notify)}
Start and Stop (Subscribe and Unsubscribe)
Observables need an API to start and stop their values. Start a timer,
stop a timer. Start listening, stop listening. Etc. But with our
current setup, the opportunity to start and stop is locked away inside
of the createObservable function.
Let’s add that functionality with the most straight-forward way possible
by creating a subscribe function and an unsubscribe and then returning
them:
const createObservable = observer => {const subscribe = () => {document.addEventListener("click", observer.notify)}const unsubscribe = () => {document.removeEventListener("click", observer.notify)}return {subscribe,unsubscribe}}observable.subscribe()observable.unsubscribe()
Unsubscribe After You Subscribe
Unfortunately, our current setup introduces a bug where you could
unsubscribe before you had called subscribe. You fix this by having
the subscribe function return an unsubscribe function. That way
you don’t have access to unsubscribe until you’ve subscribed in
the first place:
const createObservable = observer => {const subscribe = () => {document.addEventListener("click", observer.notify)//unsubscribe now lives inside of `subscribe`const unsubscribe = () => {document.removeEventListener("click", observer.notify)}return unsubscribe}return {subscribe}}const observable = createObservable(observer)const unsubscribe = observable.subscribe()unsubscribe()
Extracting subscribe for Flexibilty
You may have noticed that subscribe is a standalone, reusable function
trapped inside of createObservable. Let’s see what if would look
like with a cut n’ paste extraction refactoring:
//BROKEN: missing `subscribe`const createObservable = observer => {return {subscribe}}//BROKEN: missing `observer`const subscribe = () => {document.addEventListener("click", observer.notify)const unsubscribe = () => {document.removeEventListener("click", observer.notify)}return unsubscribe}
That almost worked, but we need to do 2 things:
- Move the
observerintosubscribe
const subscribe = () =>//becomesconst subscribe = observer =>
- Move the
subscribeintocreateObservable
const createObservable = observer =>//becomesconst createObservable = subscribe =>
We Did It! 🎉
Believe it or not, we’ve finally landed on an Observable:
const createObservable = subscribe => {return {subscribe}}const subscribe = observer => {document.addEventListener("click", observer.notify)const unsubscribe = () => {document.removeEventListener("click", observer.notify)}return unsubscribe}
You can see it in action below. A challenge for you is to create your
own subscribe functions that you can pass in to createObservable.
Try using setInterval or any other async tool that can start and stop.