Organiccat
Organiccat

Reputation: 5651

Why use event listeners over function calls?

I've been studying event listeners lately and I think I've finally gotten them down. Basically, they are functions that are called on another object's method. My question is, why create an event listener when calling the function will work just fine?

Example, I want to call player.display_health(), and when this is fired, the method player.get_health() should be fired and stored so that display_health() has access to it. Why should I use an event listener over simply calling the function? Even if display_health() were in another object, this still doesn't appear to be a problem to me.

If you have another example that fits the usage better, please let me know. Perhaps particular languages don't benefit from it as much? (Javascript, PHP, ASP?)

Upvotes: 17

Views: 10017

Answers (6)

JohnDoe
JohnDoe

Reputation: 1

Let's say you have a user that registered, and a controller that handles it. You write everything in the controller to register them and send off an email welcoming them, as well as giving them roles and other things maybe. This can become very single use case and restrictive, and require you to copy everything to another controller if you wanted to reuse the code.

Now with events and listeners (the event is really just the model in my opinion), you can have multiple events with the same model, and you name the event what you want to be the "start of the tree". The listeners are under that and will fire because that event did, and let you do things while being able to use the passed information from the event. So let's say $user in this example, as long as $user is passed on the call to the event, you can now use anything in the $user object, like $user->name, in your listeners

The benefit is that you can move a lot away from the controller. Move the role setting to a listener, move the email sending to another listener, etc. And just have 1 event called something like UserRegisteredEvent that will fire all those listeners. You can also easily reuse them in something like the admin dashboard part of the application and just call the event you already have, and all the listeners will follow. Or even make a new event and only attach the new event to some of those existing listeners, but without having to redo much code (for example, a new event called AdminPanelUserRegisteredEvent will only be attached to SendRegistrationEmailListener)

And because of that, in the future if you needed to update the registration emails to something different, you won't have to go searching all over every possible controller.. instead you just update the email listener for the the registration event and you're done. For me personally this also helps with my own popup notifications processing as almost every controller I was calling a notification utility and passing parameters to it, which became messy and difficult to update if I wanted to add new parameters or remove old ones. Now I can just call a notification event in all the controllers, and only have 1 location to worry about updating how my popup notifications work

Hopefully that kinda helps some people coming across this later

Upvotes: 0

codeasaurus
codeasaurus

Reputation: 163

I think the main reason for events vs function calls is that events are 'listened to' while calls are 'made'. So a function call is always made to another object whereas listeners 'choose' to listen for an event to be broadcast from your object. The observer pattern is a good study for this capability. Here is a brief node.js example which illustrates the concept:

var events = require('events');
var Person = function(pname) {
    var name = pname;
};


var james = new Person('james');
var mary = new Person('mary');
var loudmouth = new Person('blabberer');

loudmouth.mouth = new events.EventEmitter();

//jame's observer.
james.read_lips = function(msg){
    console.log("james found out: " + msg);
};

//james adds his event to the emitter's event listener.
james.enter_elevator = function(){
    console.log('james is in the elevator');
    //NOTE: james adds HIMSELF as a listener for the events that may
    //transpire while he is in the elevator.
    loudmouth.mouth.on('elevator gossip', james.read_lips)
};

//james removes his event from the emitter when he leaves the elevator.
james.leave_elevator = function(){
    // read lips is how james responds to the event.
    loudmouth.mouth.removeListener('elevator gossip', james.read_lips);
    console.log('james has left the elevator');
};

//mary's observer
mary.overhear = function(msg){
    console.log("mary heard: " + msg);
};

//mary adds her observer event to the emitter's event listeners
mary.enter_elevator = function(){
    // overhear is how mary responds to the event.
    console.log('mary is in the elevator');
    //NOTE: now mary adds HERSELF to the listeners in the elevator and 
    //she observes using a different method than james which suits her.
    loudmouth.mouth.on('elevator gossip', mary.overhear);
};

loudmouth.speaks = function(what_is_said){
    console.log('loudmouth: ' + what_is_said);
    this.mouth.emit('elevator gossip', what_is_said);
};

james.enter_elevator();
mary.enter_elevator();
loudmouth.speaks('boss is having an affair');
james.leave_elevator();
loudmouth.speaks('just kidding');
console.log('james did not hear the last line because he was not listening anymore =)');

so in this 'story' the actors choose to listen or when to not listen for events from a third party. I hope this helps.

Upvotes: 2

luminol
luminol

Reputation: 425

Even with the detailed answers above, I was still having trouble understanding what the actual difference was between using a controller / functions OR an event listener.

One of the things that has been left out in all of these answers is that the use of Events and Event Listeners comes in handy when you do not want to couple your code so closely. Each function, class, etc, should have singleness of purpose.

So say you are getting hit with an API request from an outsider. In my case, my exact problem understanding this concept was when I am receiving API calls from Stripe Webhooks.

The purpose of Stripe Webhooks is: say a customer spends $10,000 on your website. Your standard procedure is to Auth and Capture. Update DB to reflect their new membership status. In a perfect world, and in our company's case, 999/1000 times, this goes perfectly. Either their card is declined on the spot, or the payment goes through. In both cases, we send them an email letting them know.

But what about the 1/1000 time when the user pays and Stripe returns a Card Failure error (which can be a number of different things)? In our case, we email them and tell them the billing has failed. The problem we've encountered is that some BANKS are investigating large charges, which comes back as an Error, but then a few minutes later the bank authorizes the charges and the payment is captured.

So what is there to do? Enter Stripe Webhooks. Stripe Webhooks will hit an API endpoint if something like this occurs. Actually, Stripe Webhooks can hit your API any and every time a payment isn't instantly Authed, Captured, or if the customer asks for a refund.

This is where an Event Listener comes in handy. Stripe shoots over a POST with the customer info, as well as the Webhook type. We will now process that, update the database, and shoot them a success email.

But why not just use a standard route and controller? The reason we don't just use a standard route and controller is because we would either need to modify the already defined functions, classes, etc, or create a new series of classes that are coupled together, such as -> Stripe API Calls Received, Update DB, Send Email. Instead of coupling these closely together, we use an Event Listener to first accept the API Call, then hit each of those Classes, Functions, etc., leaving everything uncoupled.

I looked everywhere, and I think the Laravel documentation explains it best. I finally understood when given a concrete example, and what the purpose of an Event Listener is:

Events serve as a great way to decouple various aspects of your application, since a single event can have multiple listeners that do not depend on each other. For example, you may wish to send a Slack notification to your user each time an order has shipped. Instead of coupling your order processing code to your Slack notification code, you can raise an OrderShipped event, which a listener can receive and transform into a Slack notification.

https://laravel.com/docs/5.6/events

Upvotes: 4

Rashel
Rashel

Reputation: 21

In Brief, you can write the code without event listener, but using event listener help other to use the same code as library.

Upvotes: 2

David
David

Reputation: 219037

You might not always be in control of the code that's doing the calling. Or even if you are, you don't want to introduce dependencies into that code. In cases like that, it's better for the code to fire an event and allow the code you do control, or the code that should have the dependency, to listen for the event and act accordingly.

For example, perhaps you're creating a library that will be used by other people. They don't have the source code or in some way can't/shouldn't be able to modify it (or shouldn't have to). Your documentation states that specific events are raised under specific circumstances. They can then, in turn, respond to those events.

Or perhaps you have some domain libraries in your enterprise. You do control them and can modify them, but architecturally they're generally considered to be working as they currently are coded and shouldn't be changed. (Don't want to incur a round of QA to re-validate the updated code, the code belongs to another department and they don't want you to change it, etc.) And you're in the position where you want that code to be able to do different things in different circumstances/environments. If that code raises and event where relevant, you can hook your code into it (and/or swap out accordingly) without having to mess with that code.

Just a couple quick examples, I'm sure others have more.

Upvotes: 14

Anon.
Anon.

Reputation: 60033

My question is, why create an event listener when calling the function will work just fine?

What if you don't know what function you want to call?

Take the classic example, a Button that the user can click on. Whoever writes the library has no idea what function you want called when the button is clicked. It would also be pretty prohibitive if every Button could only call the same function when it is clicked.

So instead, you can attach an event handler to the event. Then when the event is triggered, the Button can do what it needs to, without having to know at compile-time exactly what function it's supposed to be calling.

Upvotes: 5

Related Questions