Why should we use Observer/Pub-Sub pattern here? - javascript

I am trying to learn Observer and publisher-subscriber pattern.
came through this simple example here
Problem: There is a button and onclick of the button it should be updating the count.
without any pattern i can do simply as
window.onload = function() {
var container = document.querySelector('.container');
var count = 0;
container.querySelector('#click').addEventListener('click', function() {
count = +document.querySelector("#count").innerHTML;
count++;
document.querySelector("#count").innerHTML = count;
});
}
<div class="container">
<input type="button" id="click" value="click">Total Counts: <span id="count">0</span>
</div>
In the above link that i have shared about observer pattern it has an implementation for the same using observer pattern jsbin
My Question here, is the usage of a pattern not complicating the code. I am really having a bad time of understanding what exactly the code is trying to solve .can some one please explain this and what is this.notify doing in the jsbin code.
Please help
Thanks

Not an expert in patterns but from what I understand, with simple code like your example that takes in a single event listener, the Observer Pattern would definitely be overkill.
As explained in your link above: "The observer pattern is a simple way to allow communication between elements without having to rely on events, callbacks, or polling. The best thing about the observer pattern is that the thing being observed does not have to worry about what is observing it or how many observers it has." It basically allows you to attach observers easily without having to modify the base element code, because the base code doesn't really have to care about who is watching it. It just has to announce that it's done something (increased a counter property) and it's up to the observers to react accordingly. Because of this, the counter code could stand on it's own and not have any dependencies to run (thus, making it easier to test as well). If you need to make changes to your observers, you won't have to touch the counter code and risk causing any side effects.
In comparison, your example has your callback code and counter heavily tied to one another. If you need to make a change like say, making it have different wording or have the counter value appear under a specific element, you have no choice but to touch that entire block of code. Again though, your code example is simple enough and if that is all it will be doing, then it should be perfectly fine to use.
I think it's easier to understand the concept of the Observer pattern when working with stuff like async code and Promises, where your callbacks/observers become separate from your implementing async code

Firstly, please make sure we are on the same page regarding the terminologies in Observer Pattern (OP): Observer object, Subject (or Observee) object, Subject.addObserver(...) method, and Subject.notify(...) method.
OK, now,
without any pattern i can do simply as
No, you are actually using OP in an implicit form. When you wrote:
container.querySelector('#click')
This will return a reference to the button, I name it button:
var button = container.querySelector('#click');
Then the call button.addEventListener(...) is basically an analogy to Subject.addObserver(...). This means that your button object is actually the Subject in OP. The call Subject.notify(...) is implicitly handled by the JavaScript engine. And your inline function to consume the click event is actually the Observer.
The main difference between your code and the code of jarrettmeyer.com lies in the question: who is the Subject? In jarrettmeyer.com, Subject is not any button but a separated object: the Counter object. This offers some advantages:
The Subject can associate with many buttons, for example, jarrettmeyer can write: $("#anotherButton").on("click", function () { counter.increment(); });
The Subject can easily maintain whatever state and notify whatever info to the Observer. In jarrettmeyer's example, these state/info are simply a count number. Indeed, in your example, no state/info of the button (except the fact that it has just been clicked) is notified since the count number is maintained in your span which belongs to the implementation detail of your Observer and thus not related to OP.

Do you know the code you wrote is also an implementation of the observer pattern? The function you passed after the 'click' argument is an observer and it is added to the observers' array. You can add as many functions as you want against the 'click' event of the same element. They all will be fired by running a loop in the observers' array when the 'click' event happens.
If you have only one action happening as a response to some other action, you can write the action manually without implementing the observer pattern. However, when you want to do multiple things at multiple parts of the codebase in response to some event, observer pattern is the way to go.

Yes, you are right. addEventListener or jQuery .on() could do the similar thing as Observer. They are good enough for most of the front-end usage. But in the following use cases (backend/abstraction), observer pattern is better:
The event being listened is not related to the DOM elements (e.g. JS object's mutation)
You would like to have a better control on removeEventListener (e.g. multiple anonymous callback functions bound on an event type, you would like to move one of them)
The .notify method in the example is made to loop all the callback function in registry array, and try to execute all of them.
Here's a Codepen to show how observer help in the real world.
And here's a simple observer implementation when I learn Observer pattern:
var App = function() {
// This array will store all the subscribers.
this.subscribers = [];
}
// Subscribe, unsubscribe and publish are three base methods in this pattern
App.prototype.subscribe = function(subscriber) {
this.subscribers.push(subscriber);
}
App.prototype.unsubscribe = function(subscriber) {
for (var i = 0; i < this.subscribers.length; i++) {
if (this.subscribers[i] === subscriber) {
this.subscribers.splice(i, 1);
}
}
}
App.prototype.publish = function(message) {
for (var i = 0; i < this.subscribers.length; i++) {
console.log(this.subscribers[i] + ' got ' + message + '!');
}
}
// Testing code.
var myApp = new App();
myApp.subscribe('Timmy');
myApp.subscribe('Tommy');
myApp.publish('a new magazine'); // Both Timmy & Tommy got the new magazine
myApp.unsubscribe('Timmy');
myApp.publish('a new book'); // Now only Tommy got the new book
Attached the Codepen for reference.

Related

Create and dispatch events javascript. Code execution simple delegation

Comming from a c# background, I just want to create an event in a certain point of my code, soas to be dispatched elsewere, meaning that if in some part of the code there has been a subscription, this delegate function is called.
So I tried to do:
function myFunction() {
console.log("delegated call achieved!");
}
const myEvent = new Event('onMyConditionIsMet', myFunction, false);
//at this point the program the subscription takes place
function whatever1() {
//...not meaningfull code
myEvent.addEventListener('onMyConditionIsMet');
//myEvent += myFunction; c# way subscription in case it makes sense
}
//at this point in the program, event subscription is checked and
//delegate func run in case there has been a subscription
function whatever2() {
//...not meaningfull code
myEvent?.invoke(); // ?.invoke(); would be the c# way to do it.
}
All the examples I found are related to DOM events, but my case would be for events I create myself, think these are called synthetic events.
Another assumption I make in this question is that there would be no arguments in the delegate call function, so, just to be clear with the naming, it would be a delegate with no arguments. Just pointing this because in c# events are just delegate funcs with no arguments, so a specific type of delegate. Not sure if this works the same way in Javscript.
What would be the approach to do this? (Meaning creating a simple event instance, subscribing, and executing the delegated code if there is any subscription)?
I think the functionality you are looking for can be best obtained by using OOP/Classes.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#prototype_methods
Edit: see this also - "event" is deprecated, what should be used instead?

How to test click event using pure javascript and Jest

I'm trying to test a really simple increment function in vanilla javascript.
This function have a button with a click event, which triggers the input to sum one to its value.
I've tried to search for help to think how to resolve this problem. I think maybe I should create a mock for the button (instead of access the DOM element), and simulates the click event with Enzyme (but I don't know if it is really necessary).
All I could get in my searches was Jest testing using components from React or Angular, which complicated much more my question and therefore I get no answer for simple JS. The Jest documentation didn't help either.
The code of my function is:
const increment = () => {
$increment.addEventListener("click", function() {
if (+$quantity.value < 100) {
$quantity.value = +$quantity.value + 1;
}
});
};
The full code is on this codesandbox.
Hokay so, my JavaScript is a little rusty but I think I know the problem looking at the code (thank you by the way, it made this way easier to figure out)...
Your instinct that you need a mock is correct, but right now the way your increment function works it's coupled to $increment which is in the local scope (making it really unfun to mock). Instead of using a private variable in the local scope to bind the event listener to, you want to pass the $element into the increment function, and then to add the event listener to it.
const increment = ($element) => {
$element.addEventListener("click", function() {
if (+$quantity.value < 100) {
$quantity.value = +$quantity.value + 1;
}
});
};
In your test now you can create a mock with a function on it called addEventListener... the below is probably not quite right, but I think should get you most of the way there:
// In your test setup, or in the test itself
const myMockElement = {
addEventListener: jest.fn(),
};
// Later in your test
increment(myMockElement);
expect(myMockElement.addEventListener.mock.calls.length).toBe(1);
Just as a note from the code in the event listener, I'd recommend passing it $quantity into the function as well instead of capturing it from the local context/scope/whatever-the-hell-its-exactly-called-in-javascript (i.e. what we did with $element)... it'll make testing things MUCH, MUCH easier to test and make your functions more robust.
Hope this helps!

rxjs using promise only once on subscribe

I wanted to use rxjs for the first time but am a bit stucked 'cause it doesn't behave exactly like I want it to: In my scenario I want to create an observable from a promise. But I want the promise only being called once (not on every subscription) and I want it not being called on creation time (defer the call to the first subscription).
First I tried this:
var source = Rx.Observable.fromPromise(_this.getMyPromise())
which causes a call to the getMyPromise function right on creation time. This is not satisfying because at that time I don't know if the source really will be used.
Then I tried:
var source = Rx.Observable.defer(function() { return _this.getMyPromise() })
which causes a call to the getMyPromise function each time a new subscription is being made to source. This makes way too many unnecessary calls to the web server. The Rx.Observable.create function seems to have the same issue.
So what is left or what am I missing?
.shareReplay() does this, e.g.:
var source = Rx.Observable.defer(function() { return _this.getMyPromise() }).shareReplay();
If you're using rxjs5, you'll want to read: Pattern for shareReplay(1) in RxJS5
In answer to your comment below, I can think of a fairly straightforward extension to the above logic that will do what you want, but it has a caveat. Let's say the events you want to use to trigger a "refresh" are represented in a stream, s$, then you could do something like:
var source = Rx.Observable.of({}).concat(s$)
.flatMapLatest(function() {
return Rx.Observable.defer(function() {
return _this.getMyPromise()
})
})
.shareReplay(1)
What we have here is a stream starting with a dummy object to get things rolling, followed by a stream consisting of your refresh events. Each of these is projected into a new observable created from a fresh invocation of your getMyPromise method, and the whole thing is flattened into a single stream. Finally, we keep the shareReplay logic so we only actually make calls when we should.
The caveat is that this will only work properly if there's always at least one subscriber to the source (the first subscription after all others are disposed will run the promise again, and will receive both the previously-cached value and the result of the promise it caused to run).
Here is an answer that does not require at least one subscriber at the source at all times using a simple helper:
var _p = null;
var once = function() { return _p || (_p = _this.getMyPromise());
var source = Rx.Observable.defer(once);
Or if you're using lodash, you can _.memoize your getMyPromise and get this automatically.

How to achieve multiple event fires & events memory (features of events & promises combined)

My requirements
Because of the asynchronous architecture of my applications I am looking for an 'event' system which has the following two two properties:
The events should be able to fire multiple times (possible with events, but not with promises)
When I start listening for an event that has already been fired, I want the listener to fire once immediately (as with promises)
The reason for 1. is that there are a lot of events (e.g. the updating of certain data) that I want to be able to fire multiple times. But I would like to combine this with 2. so that if an event has already fired upon adding the listener, this listener gets called immediately. This is because I'm not always sure (and I don't want to be sure) which piece of code gets run first.
My 'solution'
I have thought up the following solution. I'm using this in an AngularJS application therefore the AngularJS context, but the question is applicable for Javascript in general. Note that I simplified the code.
app.controller('AppCtrl', function(CustomEventEmitter){
// Broadcast an event. No listener added so nothing happens
CustomEventEmitter.broadcast('event');
// Add the event listener. Because the event allready fired, the listener gets called immediatly
CustomEventEmitter.on('event', function(){
console.log('Event emitted');
});
// Broadcast an other event
CustomEventEmitter.broadcast('event');
});
app.service('CustomEventEmitter', function(){
var
listeners = {},
memory = [];
this.broadcast = function(name){
// The normal broadcasting of the event to the listener
if(listeners[name]) {
listeners[name].forEach(function(listener){
listener();
});
}
// Push the event into the 'memory'
memory.push(name);
};
this.on = function(name, listener){
// The normal adding of the listener
if(!listeners[name]) {
listeners[name] = [];
}
listeners[name].push(listener);
// If an event is already in memory, call the listener
if(memory.indexOf(name) !== -1) {
listener();
}
};
});
My questions
My questions are these:
What is the 'best practice' solution for my requirements?
What do you think of my 'solution'?
Am I missing something completely obvious?
The reason for the last question is that it seems to me that this is a very common design paradigm but I seem unable to find the best way to solve this in simple and concise way.
Note
I understand this can be solved with the adding of extra code (e.g. before adding the listener, check in an other way if the event you are going to listen for already happened) but this is not what I'm looking for.
A "property" from bacon.js does exactly what you are asking for. This falls under the broader category of functional reactive programming (FRP). The most popular two libraries for this in JavaScript are probably
bacon.js
Reactive Extensions
Both of which provide the specific tool you're asking for, along with a vast array of alternatives.

How do I bind a function to a change in an object's data member in Javascript?

I'm working on a project in JavaScript where we're building a Greasemonkey plugin to an organizational site we're using in our office. We're having trouble getting our changes to stay rendered, since we can't simply inject our changes into the existing render function.
As a result, we need to find every event where rendering happens and inject our own render function there. However, there are some events that we can see happening, but we can't hook into them. What I'd like to know is how to bind a function to an object's data member, so that the function is called whenever that member changes. One of our team members seemed to think it was possible, but the method he told us to use didn't seem to work.
What we tried was something along the lines of
window.Controller.bind("change:idBoardCurrent", OMGITWORKED);
where idBoardCurrent is a member of window.Controller and OMGITWORKED is the function we'd like to be called when window.Controller.idBoardCurrent is changed.
I'm not very familiar with JavaScript or data binding, so I have no idea if this is right or wrong, or what is correct or incorrect about it. If someone could point out what to change in this snippet, or if they could suggest another way to go about this, I would be very appreciative.
You can use Object.defineProperty to define a setter and getter for the Objects property
Object.defineProperty(window.Controller,"idBoardCurrent",{
get : function() { return this.val; },
set : function(value) {this.val = value;OMGITWORKED(value); }
});
function OMGITWORKED(param) {
console.log("idBoardCurrent has been Changed to " + param);
}
window.Controller.idBoardCurrent = "Test";
window.Controller.idBoardCurrent = "Test2";
console.log(window.Controller.idBoardCurrent)
Edit: changed the code according to the contexts object
JSBin
As this is specifically Firefox, you can use the mutation events it provides. But note the caveats on them from that page:
The W3C specification for them was never widely implemented and is now deprecated
Using DOM mutation events "significantly degrades" the performance of DOM modifications
If you're able to restrict yourselves to Firefox 14 and higher, you can use the new mutation observers stuff instead.
This is, when I am not totally wrong, more a question of javascript.
I found some information about that topic
Listening for variable changes in JavaScript or jQuery
jQuery trigger on variable change
Javascript Track Variable Change
Sorry when I didn't understand the topic.
All the best

Categories

Resources