Most of node.js’s core apis are built on the idiomatic asynchronous event-driven architecture. Events (EventEmitter) is a subscription-based publishing design pattern.
Most modules in Node.js depend on this module, such as Net, HTTP, FS, Stream, etc. Besides these system modules, EventEmitter can also be seen in the well-known Express and Koa frameworks.
1: on & emit & once
const Events = require("events");
class MyEvent extends Events {}
var e1 = new MyEvent();
e1.on("start".(name) = > {
console.log("start:event11 "The name,this === el);
});
e1.on("start".() = > {
console.log("start:event22 ");
});
// Execute the registration function only once
el.once('eat:breskfast'.function(){
console.log('eat breakfast')
})
e1.emit("start"."anikin");
// The result is: you can listen multiple times for a single trigger
// console.log("start:event11 ", name, this === el);
// console.log("start:event22 ");
el.emit('eat:breskfast')
Copy the code
Notice the distinction between on and once:
var num = 0;
e1.on("test".function () {
console.log("test", ++num);
});
e1.emit("test"); // test,1
e1.emit("test"); // test,2
// If it is once
e1.once("test".function () {
console.log("test", ++num);
});
e1.emit("test"); // test,1
e1.emit("test"); // test,1
// Check again
e1.prependOnceListener("test".function () {
console.log("apend test", ++num);
});
e1.emit("apend test"); // test,1
e1.emit("test"); // test,2
Copy the code
The prependOnceListener method can be used to add an event listener to the beginning of the listener array. The next time I call it, I’ll know myself, so I won’t call it more than once.
2: indicates asynchronous or synchronous
EventEmitter calls all listeners synchronously in the order they are registered. This ensures the correct sequencing of events and helps avoid race conditions and logic errors. When appropriate, the listener functions can use the setImmediate() and process.nexttick () methods to switch to asynchronous operation mode:
const myEmitter = new MyEmitter();
myEmitter.on('event'.(a, b) = > {
setImmediate(() = > {
console.log('Occurs asynchronously');
});
});
myEmitter.emit('event'.'a'.'b');
Copy the code
3: the error
When an EventEmitter instance fails, an Error event should be emitted. These are treated as special cases in Node.js. If no listener is registered for the error event, when the error event is fired, an error is thrown, a stack trace is printed, and the Node.js process exits.
const myEmitter = new MyEmitter();
myEmitter.emit('error'.new Error('Error message'));
Copy the code
4: Avalanche problem under high concurrency
Query the DB data to need, we usually call it the hot spot data, this data is usually to add a layer on the DB cache, but in high concurrency scenario, if the cache is failure, at this time there will be a lot of request directly into the database, create constant pressure to the database, to cache the avalanche of solution, There are better solutions online, but in Node.js we can use the once() method provided by the Events module.
When an event of the same name is triggered multiple times, a listener added via once executes only once and touches the event associated with it after execution.
function onceWrapper(. args) {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
return Reflect.apply(this.listener, this.target, args); }}function _onceWrap(target, type, listener) {
var state = { fired: false.wrapFn: undefined, target, type, listener };
var wrapped = onceWrapper.bind(state);
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped;
}
EventEmitter.prototype.once = function once(type, listener) {
checkListener(listener);
this.on(type, _onceWrap(this, type, listener));
return this;
};
Copy the code
The once method is used to push the callback of all requests into the event queue. The same file name query is guaranteed to be only once from the beginning to the end of the same query. If it is a DB query, the database query overhead caused by repeated data is also avoided. The code is written by referring to the profound and simple Nodejs Events module book. Here fs is used for file query, and the same is true if DB is used. In addition, the status key value pair is used to save the name and status of the event triggered/listened on.
const events = require('events');
const emitter = new events.EventEmitter();
const fs = require('fs');
const status = {};
const select = function(file, filename, cb) {
emitter.once(file, cb);
if (status[file] === undefined) {
status[file] = 'ready'; // No default value is set
}
if (status[file] === 'ready') {
status[file] = 'pending';
fs.readFile(file, function(err, result) {
console.log(filename);
emitter.emit(file, err, result.toString());
status[file] = 'ready';
setTimeout(function() {
delete status[file];
}, 1000); }); }}for (let i=1; i<=11; i++) {
if (i % 2= = =0) {
select(`/tmp/a.txt`.'a file'.function(err, result) {
console.log('err: ', err, 'result: ', result);
});
} else {
select(`/tmp/b.txt`.'b file'.function(err, result) {
console.log('err: ', err, 'result: ', result); }); }}Copy the code
In fact, the above code is more tedious, simple point is to add a lock to achieve.
By default, EventEmitter prints a warning if more than 10 listeners are added for a particular event. However, not all events are limited to 10 listeners. The Emitter.setMaxListeners () method modifiers limits for a given EventEmitter instance.
Many of Node.js’s most successful modules and frameworks are based on EventEmitter, and it’s useful to learn how to use them and when to use them.
EventEmitter is essentially an implementation of the Observer pattern. A similar pattern is publish/subscribe, where the producer publishes the message and does not care about the implementation of the subscriber. RabbitMQ itself is also based on the AMQP protocol, which is a good solution in a distributed cluster environment.
Reference:
Nodejs. Cn/API/events….
Copy the code