Let's see the following code first:
let button = document.getElementById("button"); button.addEventListener("click", (e) => { console.log(e) });
We have a button, which we can attach a event listener to it. In this case, everytime we click on the button, there will be an event broadcasted out.
So this button event become a "Broadcaster".
And we have a callback function:
(e) => {
console.log(e)
}
This is the "Listener", it react to broadcaster event and trigger side effect to the world.
OK, so far, we have "Broadcaster" and "Listener", let's refactor the code to make it more clear:
let button = document.getElementById("button"); let buttonClickBroadcaster = (listener) => { button.addEventListener("click", listener); }; let logListener = (value) => { console.log(value); }; ButtonClickBroadcaster(logListener)
We make broadcaster and listener a own function:
"buttonClickBroadcaster" & "logListener".
Now if we have requirement that we want logListener log out twice for each button click event. In other word, click button once, there are two logs in the console. How should we do it?
Well, the first solution come to my mind is modify the listener.
let logListener = (value) => {
console.log(value);
console.log(value);
};
We just double the 'console.log', then job is done!
Sadly this is not best solution, it has problem:
We "break" the logListener, it no longer log once, but twice. What if we have other place use this listener? it would break for them as well. So, really, We don't want to touch the existing "Listener", we want to leave it untouched!
Then how about modify "Broadcaster"? Well, it has the same problem.
We modified the existing broadcaster logic, it is not good, we want to keep "Broadcaster" untouched as well!
Operator
This is where "Operator" comes in to play. Since we cannot modify "broadcaster" & "listener". We can only introduce a new Role: "Operator".
So what is Operator?
[Note]: picture comes from John Lindquist workshop: https://docs.google.com/presentation/d/1-ShMBAImLCv1Pkr7DJ7OPCUPlv9ZMnnEayl8NGrlg5Q/edit#slide=id.g851aa9b068_6_0
So "Operator", stay between "Broadcaster" & "Listener".
And we can chian multi operators together:
Or even one operator can have multi listeners:
Or multi broadcasters info one operator:
So by using "Operator", we can solve the previous problem we have which we want to log out button click event twices.
How to do that?
We create a TwiceOperator, which listen to "Broadcaster", when we got data from "Broadcaster", in "TwiceOperator", we call "Listener" twice.
let twiceOpertaor = (broadcaser) => (listener) => { broadcaser((v) => { listener(v); listener(v); }); }; twiceOpertaor(buttonClickBroadcaster)(logListener);
In this approach, we keep "Listener" & "Broadcaster" untouched, and only modify the value inside "Opeator". This would make code more reuseable and testable.
FullCode:
let button = document.getElementById("button"); let buttonClickBroadcaster = (listener) => { button.addEventListener("click", listener); }; let logListener = (value) => { console.log(value); }; // opeator give you a chance to re-behavior the listener // do whatever changes your want let twiceOpertaor = (broadcaser) => (listener) => { broadcaser((v) => { listener(v); listener(v); }); }; twiceOpertaor(buttonClickBroadcaster)(logListener);
Summary:
Introudce "Operator", we can keep the original "Broadcaster" & "Listener" code unchnaged. And also give us capability to modify the source before the value send to "Listener".
So "Operator" takes one or multi "Broadcaster", return a enhacned "Broadcaster"! Similar idea to Typescript decorator, Proxy or React High order component
And of course, it uses lot of Functional Programming concepts which we will see in the future posts.