Implementation of event binding in Node.js using event emitter mode _node.js

Source: Internet
Author: User
Tags anonymous emit eval event listener generator http request readfile socket

In node, many objects will launch events. For example, a TCP server emits a "connect" event whenever a client requests a connection, or, for example, whenever a whole piece of data is read, the file system emits a "data" event. These objects are referred to as event emitters in node emitter. The event emitter allows programmers to subscribe to events of interest to them and bind the callback function to related events so that the callback function is invoked whenever an event emitter launches an event. The Publish/Subscribe mode is very similar to the traditional GUI mode, for example, when the button is clicked, the program receives the corresponding notification. Using this pattern, a server-side program can react when events occur, such as having a client connection, having data available on the socket, or when the file is closed.

You can also create your own event launcher, in fact, node specifically provides a Eventemitter pseudo class that can be used as a base class to create its own event launcher.

Understanding Callback Patterns

Asynchronous programming does not use function return values to indicate the end of a function call, but rather the subsequent delivery style.

"Successor Style" (Cps:continuation-passing style) is a programming style in which process control is explicitly passed to the next step ...

The CPS-style function takes a function as an extra argument, this function is used to explicitly point out the next process of program control, and when the CPS function calculates its "return value", it invokes the function that represents the next process of the program and takes the "return value" of the CPS function as its argument.

From Wikipedia--http://en.wikipedia.org/wiki/continuation-passing_style

In this programming style, each function calls a callback function at the end of execution, so that the program can continue to run. As you'll see later on, JavaScript is perfect for this programming style, and here's an example of loading a file into memory under node:

Copy Code code as follows:

var fs = require (' FS ');

Fs.readfile ('/etc/passwd ', function (err, filecontent) {

if (err) {

throw err;

}

Console.log (' file content ', filecontent.tostring ());

});

In this example, you pass an inline anonymous function as the second parameter of Fs.readfile, which is actually programming with CPS, because you give the callback function the subsequent process of executing the program.

As you can see, the first parameter of the callback function is an error object, and if the program is wrong, this parameter will be an instance of an error class, a common pattern in node's CPS programming.

Understanding Event Emitter Patterns

In the standard callback pattern, a function is passed as a parameter to the function to be executed, which works well in the scenario where the client needs to be notified when the function is complete. However, this pattern is not appropriate if more than one event occurs during the execution of a function or if multiple occurrences occur repeatedly. For example, if you want to be notified each time the socket receives the available data, you will find that the standard callback pattern is not working well, and the event emitter pattern comes in handy, and you can use a standard interface to clearly separate the event generator from the event listener.

When you use the event generator pattern, two or more objects-the event emitter and one or more event listeners-are involved.

An event emitter, as the name suggests, is an object that can generate events. The event listener is the code that is bound to the event emitter to listen for specific types of events, as in the following example:

Copy Code code as follows:

var req = http.request (options, function (response) {

Response.on ("Data", function (data) {

Console.log ("Some data from the response", data);

});

Response.on ("End", function () {

Console.log ("response ended");

});

});

Req.end ();

This code demonstrates two necessary steps to create an HTTP request to access a remote HTTP server using the node's Http.request API (see later chapters). The first line uses a "successor style" (cps:continuation-passing style) that passes an inline function that is invoked when an HTTP response is made. The HTTP request API uses CPS here because the program needs to continue following after the Http.request function has finished executing.

When the http.request is finished, the anonymous callback function is invoked and the HTTP response object is passed as a parameter to the HTTP response object, which is an event emitter, which, depending on the node document, can launch many events, including Data,end, and the callback functions that you register will be Call.

As an experience, use the CPS mode when you need to regain execution after the requested operation completes, and use the event emitter mode when the event can occur more than once.

Understanding Event Types

The emitted event has a type that is represented by a string, and the previous example contains the "Data" and "End" two event types, which are arbitrary strings defined by the event emitter, but generally the event type consists of lowercase words that do not contain a null character.

There is no code to infer what types of events the event emitter can produce, because the event emitter API does not have an introspection mechanism, so the API you use should have a document indicating that it can launch those types of events.

Once an event occurs, the event emitter invokes the listener associated with the event and passes the relevant data as a parameter to the listener. In the previous Http.request example, the "Data" event callback function takes a data object as its first and only parameter, and "end" does not accept any data, which as part of the API contract is also subjectively defined by the author of the API, The parameter signatures of these callback functions are also described in the API documentation for each event emitter.

Although the event emitter is an interface for all types of event services, the "error" event is a special implementation in node. Most event emitters in node generate an "error" event when an error occurs, and if the program does not listen for an event emitter's "error" event, the event emitter will notice and throw an unhandled exception up when the error occurs.

You can test the effect by running the following code in node Perl, which simulates an event emitter that produces two kinds of events:

Copy Code code as follows:

var em = new (Require (' events '). Eventemitter) ();

Em.emit (' event1 ');

Em.emit (' ERROR ', new error (' My mistake '));

You will see the following output:

Copy Code code as follows:

var em = new (Require (' events '). Eventemitter) ();

Undefined

> Em.emit (' event1 ');

False

> Em.emit (' Error ', new error (' My mistake ');

Error:my mistake

At repl:1:18

At Replserver.eval (repl.js:80:21)

At repl.js:190:20

At Replserver.eval (repl.js:87:5)

At Interface.<anonymous> (repl.js:182:12)

At Interface.emit (events.js:67:17)

At Interface._online (readline.js:162:10)

At Interface._line (Readline.js:426:8)

At Interface._ttywrite (readline.js:603:14)

At Readstream.<anonymous> (readline.js:82:12)

>

Code line 2nd, randomly fired a call "Event1" event, no effect, but when the "error" event, the error was thrown to the stack. If the program is not running in the Perl command-line environment, the program will crash because of an unhandled exception.

Using the Event Emitter API

Any object that implements the event emitter pattern (such as a TCP socket,http request, etc.) implements the following set of methods:

Copy Code code as follows:

. AddListener and. on--Add event listeners for events of the specified type
. once--bind an event listener that executes only once for a specified type of event
. removeeventlistener--deletes a listener that is bound to a specified event
. removealleventlistener--deletes all listeners bound to the specified event

Let's describe them in detail below.

Use the. AddListener () or. On () binding callback function

By specifying the event type and the callback function, you can register the action that was performed when the event occurred. For example, when a file reads a data stream, if there is a block of data available, a "data" event is emitted, and the following code shows how to let the program tell you that a data event occurred by passing in a callback function.

Copy Code code as follows:

function Receivedata (data) {

Console.log ("Got data from file read stream:%j", data);

}

Readstream.addlistener ("Data", receivedata);

You can also use. On, which is just a shorthand for. AddListener, and the following code is the same as the above:

Copy Code code as follows:

function Receivedata (data) {

Console.log ("Got data from file read stream:%j", data);

}
Readstream.on ("Data", receivedata);

The preceding code, using a predefined named function as the callback function, you can also use an inline anonymous function to simplify the code:

Copy Code code as follows:

Readstream.on ("Data", function (data) {

Console.log ("Got data from file read stream:%j", data);

});


As mentioned earlier, the number and signature of the parameters passed to the callback function depend on the specific event emitter object and event type, they are not standardized, the "data" event may pass a data buffer object, "error" event passing an Error object, data stream "End" event does not pass any data to the event listener.

To bind multiple event listeners

The event emitter mode allows multiple event listeners to monitor the same event type for the same event emitter, such as:

Copy Code code as follows:

I have some data here.

I have some data here too.

The event emitter is responsible for calling all listeners bound by the specified event type in the registration order of the listener, which means:

1. An event listener may not be called immediately after an event occurs, and there may be other event listeners being invoked before it.
2. An exception thrown to the stack is an abnormal behavior, possibly because there is a bug in the code, and if an event listener throws an exception when it is invoked, it may cause some event listeners to never be invoked. In this case, the event emitter catches the exception and perhaps handles it.

Look at the following example:

Copy Code code as follows:

Readstream.on ("Data", function (data) {

throw new Error ("Something Wrong has happened");

});

Readstream.on ("Data", function (data) {

Console.log (' I have some data here too. ');

});

Because the first listener throws an exception, the second listener is not invoked.

Remove an event listener from the event emitter with. RemoveListener ()

If you no longer care about an event for an object, you can cancel the registered event listener by specifying the event type and callback function, like this:

Copy Code code as follows:

function Receivedata (data) {

Console.log ("Got data from file read stream:%j", data);

}

Readstream.on ("Data", receivedata);

// ...

Readstream.removelistener ("Data", receivedata);

In this example, the last line removes an event listener that may be invoked at any time in the future from an event emitter object.

To remove the listener, you must name the callback function because the name of the callback function is required when adding and deleting.

Use. Once () to have the callback function run at most once

You can use the. Once () function if you want to listen for a maximum number of events to perform, or for the first time in an event to occur.

Copy Code code as follows:

function Receivedata (data) {

Console.log ("Got data from file read stream:%j", data);

}

Readstream.once ("Data", receivedata);

The above code, the Receivedata function will only be invoked once. If the Readstream object emits a data event, the Receivedata callback function will and will only be triggered once.

It's just a handy way to do it, because it's simple enough to do it, like this:

Copy Code code as follows:

var eventemitter = require ("Events"). Eventemitter;

EventEmitter.prototype.once = function (type, callback) {

var that = this;

This.on (type, function listener () {

That.removelistener (type, listener);

Callback.apply (that, arguments);

});

};

In the code above, you reset the EventEmitter.prototype.once function and also redefine the once function for each object that inherits from Eventemitter. The code simply uses the. On () method to cancel the registration of the callback function and invoke the original callback function once the event is received. RemoveEventListener ().

Note: the Function.apply () method is used in the preceding code, which takes an object and takes it as the containing this variable and an array of arguments. In the preceding example, the unmodified parameter array is passed transparently to the callback function through the event emitter.

Remove all event listeners from the event launcher with. Removealllisteners ()

You can remove all listeners registered to the specified event type from the event launcher as follows:

Copy Code code as follows:

Emitter.removealllisteners (type);

For example, you can cancel all processes that interrupt the signal listener:

Copy Code code as follows:

Process.removealllisteners ("Sigterm");

Note: As an experience, it is recommended that you use this function only when you know exactly what is being deleted, otherwise you should have the other parts of the application remove the event listener collection, or you can have the parts of the program responsible for removing the listener themselves. However, in some rare scenarios, this function is useful, such as when you are ready to shut down an event emitter in an orderly fashion or close the entire process.

Create an event emitter

Event launchers are a great way to make programming interfaces more generic, in a common, understandable programming pattern where clients call functions directly, and in event emitter mode, the client is bound to various events, which makes your program more flexible. (Translator Note: This is not very confident, posted the original: The event emitter provides a great way of making a programming interface more generic. When your use a common understood pattern, clients bind to events instead of invoking functions, making your program more F Lexible.)

In addition, by using the event launcher, you can also get many features, such as binding multiple unrelated listeners on the same event.

Inherit from node event emitter

If you are interested in node's event emitter pattern and intend to use it in your own application, you can create a pseudo class by inheriting Eventemitter:

Copy Code code as follows:

Util = require (' util ');

var eventemitter = require (' Events '). Eventemitter;

This is the MyClass constructor:

var MyClass = function () {

}

Util.inherits (MyClass, Eventemitter);

Note: Util.inherits established the MyClass chain, allowing your MyClass instance to use the Eventemitter prototype method.

Launch events

By inheriting from the Eventemitter,myclass you can launch events like this:

Copy Code code as follows:

MyClass.prototype.someMethod = function () {

This.emit ("Custom Event", "Argument 1", "Argument 2");

};

The above code, when the Somemethond method is called by a MyClass instance, launches an event called "Cuteom event", which also emits two strings as data: "Argument 1" and "Argument 2", They will be passed as parameters to the event listener.

Clients of the MyClass instance can listen to the custom event event like this:

Copy Code code as follows:

var myinstance = new MyClass ();

Myinstance.on (' Custom event ', function (str1, str2) {

Console.log (' Got a custom event with the str1%s and str2%s! ', str1, str2);

});

For example, you can create a ticker class that launches a "tick" event per second:

Copy Code code as follows:

var util = require (' util '),

Eventemitter = require (' Events '). Eventemitter;

var ticker = function () {

var self = this;

SetInterval (function () {

Self.emit (' tick ');

}, 1000);

};

Util.inherits (ticker, eventemitter);

Clients using the ticker class can show how to use the ticker class and listen for "tick" events,

Copy Code code as follows:

var ticker = new Ticker ();

Ticker.on ("tick", function () {

Console.log ("tick");

});

Summary

The event emitter pattern is a reentrant mode (recurrent pattern) that can be used to decouple an event emitter object from the code of a particular set of events.

You can use Event_emitter.on () to register the listener for a specific type of event and unregister it with Event_emitter.removelistener ().

You can also create your own event launchers by inheriting Eventemitter and simply using the. Emit () function.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.