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
observer
intosubscribe
const subscribe = () =>//becomesconst subscribe = observer =>
- Move the
subscribe
intocreateObservable
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.