Js-based event management (subscribe – publish) —event-mange
About the event
When we develop in javascript, we often use a lot of events, like clicks, keyboards, mice, and so on, physical events. And what we’re talking about today, what I call events, is another form of event, subscription, publication, also known as the observer pattern, which defines a one-to-many dependency, where when an object’s state changes, all objects that depend on it get notified, whereas in javascript, It is customary to replace the publish-subscribe model with an event model.
Let me give you an example from life to help you understand this pattern. Hot summer, my mother cooked a meal on the table, steaming hot, then my mother called Xiaoming to eat (Xiaoming in the next room hungry to eat chicken at night…) “Xiao Ming came out and said to his mother,” Wait a while, the rice is cold, “then call me, too hot… Ten minutes later… Mother called you ‘rice is cold’, come to eat, and then Xiao Ming heard her mother’s shout said ‘rice is cold’, they quickly came out to eat. This example is the subscription-publish model described above. In this case, Xiao Ming is the subscriber (subscribing to ‘cold food’) and her mother is the publisher (publishing the signal ‘cold food’).
Using subscriptions – the publishing model has obvious advantages: subscribers don’t have to ask publishers if their meal is cold every second, at the right point in time, publishers will notify subscribers that their meal is cold and they can come and eat. In this way, xiaoming and her mother do not need to be coupled together, when xiaoming’s younger brother and sister want to eat in the meal cold, just tell her mother. There’s a subscription that every reader will no doubt be familiar with: publishing: binding DOM events
document.body.addEventListener('click'.function (e) {
console.log('I executed... ')},false)
Copy the code
Back to business:
event-mangeThrough the subscription-publish model
Step by step
The main methods of event-Mange module are:
- On: Subscriber, add event
- Emit: Publisher, go event
- Once: subscriber that adds events that can only be listened on once and then expire
- RemoveListener: Remove a single subscription (event)
- RemoveAllListener: Deletes a subscription for a single event type or all subscriptions
- GetListenerCount: Get the number of subscribers
The main attributes of the event-Mange module are:
- MaxEventListNum: Sets the maximum number of subscribers for a single event (default: 10)
Basic skeleton
First, we want to subscribe to and publish events via event.on and event.emit. We want to create an event instance through the constructor, and on and emit are the two methods of this instance.
function events () {};
// Enumerate the methods to the event object we want to implement
event.prototype.on = function () {};
event.prototype.emit = function () {};
event.prototype.once = function () {};
event.prototype.removeListener = function () {};
event.prototype.removeAllListener = function () {};
event.prototype.getListenerCount = function () {};
Copy the code
It looks like something’s missing, yeah, the Event object that we listed above, the MaxEventListNum property, let’s fill it in, okay
function event () {
// Because the MaxEventListNum attribute can be set by the developer
// So in the absence of set, we set it to undefind
this.MaxEventListNum = this.MaxEventListNum || undefined;
// If set is not set, we cannot make the number of listeners infinite
// This may cause memory overflow
// So we set the default value to 10 (of course, we can set it to anything else)
this.defaultMaxEventListNum = 10;
}
Copy the code
Here, basically we want to achieve the time management module attributes and methods of the initial state is about the same, that is to say, the skeleton out, we need to fill his code logic, let him become flesh and blood (looks like a life…)
It’s worth thinking, once we’ve built the skeleton, what we’re going to do is a subscription-publish model, how are we going to remember a lot of subscription events? First of all, for a subscription, we need to have a subscription type, which is topic, and for that topic we need to put all the events that subscribe to that topic together, yes, we can choose Array, a preliminary construct
event_list: {
topic1: [fn1, fn2, fn3 ...] . }Copy the code
So let’s add the event_list that holds our event to the code as an event attribute
function event () {
// Here we make a simple judgment to avoid some unexpected errors
if(!this.event_list) {
this.event_list = {};
}
this.MaxEventListNum = this.MaxEventListNum || undefined;
this.defaultMaxEventListNum = 10;
}
Copy the code
On method implementation
event.prototype.on = function () {};
Copy the code
The analysis shows that the ON method should first receive a subscribed topic, followed by a callback method that is triggered when the topic responds
event.prototype.on = function (eventName, content) {};
Copy the code
EventName is the event type, as an attribute of event_list, and all listeners of eventName type are pushed into the eventName array.
event.prototype.on = function (eventName, content) {... var _event, ctx; _event =this.event_list;
// Check if event_list exists again and reassign if it does not
if(! _event) { _event =this.event_list = {};
} else {
// Get the listener for the current eventName
ctx = this.event_list[eventName];
}
// Check whether the listener type exists
// If it does not exist, this event is being listened for the first time
// Assign the callback function content directly
if(! ctx) { ctx =this.event_list[eventName] = content;
// Change the number of subscribers
ctx.ListenerCount = 1;
} else if (isFunction(ctx)) {
// Determine if this attribute is a function (if it is a function, it already has one subscriber)
// Convert this eventName type from a function to an array
ctx = this.event_list[eventName] = [ctx, content];
// The number of subscribers changes to the array length
ctx.ListenerCount = ctx.length;
} else if (isArray(ctx)) {
// Check whether it is an array. If it is an array, push itctx.push(content); ctx.ListenerCount = ctx.length; }... };Copy the code
Once method implementation
event.prototype.once = function () {};
Copy the code
The once method executes only once for subscribed events and immediately deletes the subscription callback function from the corresponding subscription type attribute in event_list. The stored procedure is almost identical to that of the ON method, which also requires a topic of the subscription type and a callback content in response to the event
event.prototype.once = function (eventName, content) {};
Copy the code
The execution of the registered immediately after the event callback to cancel this subscription, but if the same type of event registration multiple callback monitoring, we can’t accurately to delete the current registered once method to monitor the callback, so we usually use queue traversal event listeners, find the corresponding listen callback and then delete them doesn’t work. Fortunately, the great javascript language provides us with a powerful closure feature that decorates the Content in a way that wraps it into an entirely new function.
events.prototype.once = function (event, content) {...// Once and on have the same stored event callback mechanism
// the dealOnce function wraps the function
this.on(event, dealOnce(this, event, content)); . }// Wrap the function
function dealOnce(target, type, content) {
var flag = false;
// Through the closure feature (which stores external function references in scope)
function packageFun() {
// When this listener callback is invoked, the callback method is deleted first
this.removeListener(type, packageFun);
if(! flag) { flag =true;
// Because of the closure, the original listener callback will remain, so it will still be executed
content.apply(target, arguments);
}
packageFun.content = content;
}
return packageFun;
}
Copy the code
The implementation of once actually rewraps the callback we passed ourselves and binds it to the wrapped function. The wrapped function removeListener() first removes the binding between the callback and the event, and then executes the callback
Emit method implementation
event.prototype.emit = function () {};
Copy the code
The EMIT method is used to publish events, and the driver executes the corresponding listener callback in the event listener queue, so we need a topic of event type
event.prototype.emit = function (eventName[,message][,message1][,...]) {};
Copy the code
Of course, a publish event can also pass an unlimited number of parameters to the listener, which will be passed in turn to all listener callbacks
event.prototype.emit = function (eventName[,message]) {
var _event, ctx;
// Except for the first parameter eventNmae, the other parameters are stored in an array
var args = Array.prototype.slice.call(arguments.1);
_event = this.event_list;
// Check whether the stored event queue exists
if (_event) {
// If so, get this listener type
ctx = this.event_list[eventName];
}
// Detect the event queue for this listening type
// Return if it does not exist
if(! ctx) {return false;
} else if (isFunction(ctx)) {
// If it is a yam, execute it directly and pass all arguments to this function (callback function)
ctx.apply(this, args);
} else if (isArray(ctx)) {
// is an array traversal call
for (var i = 0; i < ctx.length; i++) {
ctx[i].apply(this, args); }}};Copy the code
Emit should be easier to understand, just find the appropriate type of listener event queue from the object where the event is stored and execute each callback in the queue
RemoveListener method implementation
event.prototype.removeListener = function () {};
Copy the code
Delete a listener callback of a listener type. Obviously, we still need an event type and a listener callback that is removed when the callback in the event pair column is the same as that callback
event.prototype.removeListener = function (eventName, content) {};
Copy the code
Note that if we do have a callback to remove a listening event, we must not use anonymous functions as callbacks in the ON method. This will cause removeListener to fail because anonymous functions are not equal in javascript.
// Remove it if needed
/ / error
event.on('eatting'.function (msg) {});/ / right
event.on('eatting', cb);
/ / callback
function cb (msg) {... }Copy the code
event.prototype.removeListener = function (eventName, content) {
var _event, ctx, index = 0;
_event = this.event_list;
if(! _event) {return this;
} else {
ctx = this.event_list[eventName];
}
if(! ctx) {return this;
}
// If the function is directly delete
if (isFunction(ctx)) {
if (ctx === content) {
delete_event[eventName]; }}else if (isArray(ctx)) {
// Array traversal
for (var i = 0; i < ctx.length; i++) {
if (ctx[i] === content) {
// listen back to phase etc
// Starting with the index of the listening callback, subsequent callbacks overwrite previous callbacks
// Delete the last callback
// This is equivalent to directly deleting the listener callback that meets the condition
this.event_list[eventName].splice(i - index, 1);
ctx.ListenerCount = ctx.length;
if (this.event_list[eventName].length === 0) {
delete this.event_list[eventName] } index++; }}}};Copy the code
RemoveAllListener method implementation
event.prototype.removeAllListener = function () {};
Copy the code
EventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName: eventName
event.prototype.removeAllListener = function ([,eventName]) {
var _event, ctx;
_event = this.event_list;
if(! _event) {return this;
}
ctx = this.event_list[eventName];
// Check whether there are arguments
if (arguments.length === 0&& (! eventName)) {/ / no parameters
// Convert the key to an array and iterate over it
// Delete all type listeners in turn
var keys = Object.keys(this.event_list);
for (var i = 0, key; i < keys.length; i++) {
key = keys[i];
delete this.event_list[key]; }}// Remove parameters directly
if (ctx || isFunction(ctx) || isArray(ctx)) {
delete this.event_list[eventName];
} else {
return this; }};Copy the code
The main implementation idea is basically the same as above, there is something missing, oh, is the handling of whether the maximum number of ships is exceeded in the ON method
.// Checks if the callback queue has the maxed attribute and if it is false
if(! ctx.maxed) {// Comparisons will only be made if there is an array
if (isArray(ctx)) {
var len = ctx.length;
if (len > (this.MaxEventListNum ? this.MaxEventListNum : this.defaultMaxEventListNum)) {
// When the maximum limit is exceeded, an exception warning is issued
ctx.maxed = true;
console.warn('events.MaxEventListNum || [ MaxEventListNum ] :The number of subscriptions exceeds the maximum, and if you do not set it, the default value is 10');
} else {
ctx.maxed = false; }}}...Copy the code
Events-manage can also be used globally in Vue
events.prototype.install = function (Vue, Option) {
Vue.prototype.$ev = this;
}
Copy the code
There is no need to explain more, I think the viewer should understand how to use it (in Vue)
For more specific and detailed usage documentation of this library,Just go right here.
Code word is not easy ah, if you think there is some help, but also please give a big praise 👍 ha ha
(… It’s early morning…)