on
16-Apr-2015
15:25
- edited on
05-Jun-2023
22:30
by
JimmyPackets
Node.js has it's basis in asynchronous programming. Also known as Event-driven programming, asynchronous development gives the programmer a way to write a program in such a way as to not block the progress of the program while waiting for a long-lived task to complete. Input/Output processing for local resources such as file system access or for remote network based content such as database queries or web requests is a prime example of a operation that can take an extended period to complete.
Events are a way for the underlying system to notify the calling program that a certain long lived task is finished and ready for the calling program to act on that operation.
In traditional synchronous programming, if you were to make a database request, you'd do something like this
result = query('select * from very_large_table where complex_criteria...') process_result(result); ... do_more_work
In this example, the calling process will block and not be able to perform any other processing until the result is returned from the query. A more elegant way to do this would be to use a callback, or Event.
query_finished = function(result) { process_result(result); } query('select * from very_large_table where complex_criteria...', query_finished) ... do_more_work
In this second example, we are using event callbacks. The calling program passes in a function to the query method to call back to when the long lived operation is completed. Meanwhile the program can continue doing work and when the query is completed, the query_finished function is called and the result can be processed.
Events require 2 components, a "emitter" and a "receiver". The receiver indicates to the emitter which events it would like to receive, and the emitter sends those events to the receiver when the event has occurred. In Node, many objects emit events. For instance, a TCP connection can emit a "connected" event when a new connection is made, or a HTTP request can emit a "data" event each time a new batch of data is read from it's connection.
In my first example above, a return value is used to pass back the status of the operation. The asynchronous alternative is to use what is called "Continuous-passing style" or "CPS". For a CPS styled method, the function takes an extra argument which is a "continuation" for the completion of the function.
The standard callback pattern shown above works well when you want to let the client know of command completion, but it not optimal when several events take place during the execution, or if the events happen numerous times. This is where the Event Emitter pattern comes into play. There are two pieces of the Event Emitter pattern, the event emitter and the event listeners (where there can be more than one).
To illustrate this, let's consider the above example of a long lived SQL query. If the result contains a very large record set, it would likely not be optimal to return the whole result set in a single response from the function call. Best practice would be to break the data up into smaller chunks. For a SQL query, that could equate to rows in the resulting table. In this case, we'd want to have an event that was emitted for each row in the result set and then one event for when the result set is complete. This could be accomplished with something like this:
process_row = function(row_data) { // do something with row }; process_end = function() { // do something to when data is complete }; query('select * from very_large_table where complex_criteria...', function(response) { response.on("row", function(row_data) { process_row(row_data); }); response.on("end", function() { process_end(); }); });
As a general rule of thumb, you should use CPS when you want to regain control after an operation completes and use the emitter pattern when an event can happen more than once per method call.
For information on the core Events classes, look at the Events section in the Node.JS Manual and Documentation. the EventEmitter class is used to setup your object for emitting events to receivers. There is a good tutorial on using the EventEmitter class over at hacksparrow.com under Node.js EventEmitter Tutorial
You might also want to check out my article on Node.JS callbacks which has some more details on how to use callback functions.