1. UNIT – III
• Using Events, Listeners, Timers, and Callbacks in Node.js:Node.js
Event Model, Adding Work to the Event Queue, Implementing
Callbacks
• Implementing HTTP Services in Node.js: Processing URLs,
Understanding Request, Response, and Server Objects, Implementing
HTTP Clients and Servers in Node.js, Implementing HTTPS Servers and
Clients
• Implementing Express in Node.js: Getting Started with Express,
Configuring Routes, Using Request/Responses Objects
2. Using Events, Listeners, Timers, and Callbacks
in Node.js
• Node.js provides scalability and performance through its powerful
event-driven model.
• Node.js applications run on a single thread rather than multiple
threads
• Using the single thread for application processing makes Node.js
processes more efficient and faster.
3. Summary
• This chapter focuses on understanding the model and how it differs
from traditional threading models used by most webservers.
• events can be added to the event queue when blocking I/O is called.
And listeners can be triggered by events or timers or called directly
using the nextTick() method.
• This chapter also covers the different methods you use to add work to
the Node.js event queue. You can add work using event listeners or
timers, or you can schedule work directly
4. • Node.js applications are run in a single-threaded event-driven
model. Although Node.js implements a thread pool in the
background to do work, the application itself doesn’t have any
concept of multiple threads.
5. Traditional Threaded web model
• In the traditional threaded web model, a request comes in to the
webserver and is assigned to an available thread. Then the handling of
work for that request continues on that thread until the request is
complete and a response is sent.
• Figure illustrates the threaded model processing two requests, GetFile and
GetData.
• The GetFile request first opens the file, reads the contents, and then sends
the data back in a response. All this occurs in order on the same thread.
• The GetData request connects to the DB, queries the necessary data, and
then sends the data in the response.
7. • Node.js is a single-threaded runtime environment, but it also has multithreading
APIs.
• Single-threaded
• Node.js runs JavaScript code in a single thread, which means that it can only do one task at
a time
• The event loop, also known as the main thread, runs one thing at a time
• Single-threaded programming models are simpler to reason about
• Multithreaded
• Node.js exposes multithreading APIs, including the worker_threads module
• Node.js provides hidden threads through the libuv library, which handles I/O operations
• Advantages of single-threaded
• Reduces the overhead of thread communication
• Allows for a more quick response
• Can handle thousands of concurrent connections with minimal resource usage
• Well-suited for I/O-heavy applications, such as web servers, APIs, and real-time applications
8. • The Node.js event model does things differently. Instead of executing all
the work for each request on individual threads, work is added to an
event queue and then picked up by a single thread running an event loop.
• The event loop grabs the top item in the event queue, executes it, and
then grabs the next item. When executing code that is no longer live or
has blocking I/O, instead of calling the function directly, the function is
added to the event queue along with a callback that is executed after the
function completes.
• When all events on the Node.js event queue have been executed, the
Node application terminates.
10. • Figure illustrates the way Node.js handles the GetFile and GetData requests.
• The GetFile and GetData requests are added to the event queue.
• Node.js first picks up the GetFile request, executes it, and then completes by
adding the
• Open() callback function to the event queue.
• Next, it picks up the GetData request, executes it, and completes by adding the
Connect() callback function to the event queue. This continues until there are no
callback functions to be executed.
• Notice in Figure that the events for each thread do not necessarily follow a direct
interleaved order. For example, the Connect request takes longer to complete
than the Read request, so Send(file) is called before Query(db).
12. Blocking I/O in Node.js
• The Node.js event model of using the event callbacks is great until
you run into the problem of functions that block waiting for I/O.
Blocking I/O stops the execution of the current thread and waits for a
response before continuing.
Some examples of
• blocking I/O are
• Reading a file
• Querying a database
• Socket request
• Accessing a remote service
15. Node.js event model
• Figure illustrates the full Node.js event model including the
• Event queue,
• Event loop,
• and the thread pool.
• Notice that the event loop either executes the function on the event
loop thread itself or, for blocking I/O, it executes the function on a
separate thread.
16. Node.js event model
• Figure In the Node.js event model, work is added as a function with
callback to the event queue, and then picked up on the event loop
thread.
• The function is then executed on the event loop thread in the case of
non-blocking, or on a separate thread in the case of blocking
17. • Node.js uses event callbacks is not to have to wait for blocking I/O.
• Therefore, any requests that perform blocking I/O are performed on a
different thread in the background. Node.js implements a thread pool
in the background.
• When an event that requires blocking I/O is retrieved from the event
queue, Node.js retrieves a thread from the thread pool and executes
the function there instead of on the main event loop thread. This
prevents the blocking I/O from holding up the rest of the events in the
event queue.
18. • The function executed on the blocking thread can still add events
back to the event queue to be processed. For example, a database
query call is typically passed a callback function that parses the results
and may schedule additional work on the event queue before sending
a response.
19. Adding Work to the Event Queue
• As you create your Node.js applications, keep in mind the event
model
• To leverage the scalability and performance of the event model, make
sure that you break work upinto chunks that can be performed as a
series of callbacks.
• Once you have designed your code correctly, you can then use the
event model to schedule work on the event queue.
20. • In Node.js applications, work is scheduled on the event queue by
passing a callback function using one of these methods:
1. Make a call to one of the blocking I/O library calls such as writing to
a file or connecting to a database.
2. Add a built-in event listener to a built-in event such as an
http.request or server.connection.
3. Create your own event emitters and add custom listeners to them.
4. Use the process.nextTick option to schedule work to be picked up
on the next cycle of the event loop.
5. Use timers to schedule work to be done after a particular amount of
time or at periodic intervals.
21. Implementing Timers
• A useful feature of Node.js and JavaScript is the ability to delay
execution of code for a period of time. This can be useful for cleanup
or refresh work that you do not want to always be running.
• There are three types of timers you can implement in Node.js:
1. timeout,
2. interval,
3. and immediate.
22. Delaying Work with Timeouts
• Timeout timers are used to delay work for a specific amount of time.
When that time expires, the callback function is executed and the
timer goes away.
• Use timeouts for work that only needs to be performed once.
• Timeout timers are created using the setTimeout(callback,
• delayMilliSeconds, [args]) method built into Node.js.
• When you call setTimeout(), the callback function is executed after
delayMilliSeconds expires.
23. • The setTimeout() function returns a timer object ID. You can pass this
ID to clearTimeout(timeoutId) at any time before the
delayMilliSeconds expires to cancel the timeout function. For
example:
• myTimeout = setTimeout(myFunc, 100000);
• … clearTimeout(myTimeout);
25. • events can be added to the event queue when blocking I/O is called.
And you learned that listeners can be triggered by events or timers or
called directly using the nextTick() method.
26. • A normal function is called directly, while a callback function is initially only defined.
The callback function is only called and executed once a specific event has occurred
function getDetails(){
setTimeout(() => {
console.log("DETAILS")
}, 2000);
}
function getUser(){
setTimeout(() => {
console.log("USER");
callback(); // Calling the function
}, 3000);
}
getUser(getDetails);
27. Performing Periodic Work with Intervals
• Interval timers are used to perform work on a regular delayed
interval. When the delay time expires, the callback function is
executed and is then rescheduled for the delay interval again.
• Use intervals for work that needs to be performed on a regular basis.
• Interval timers are created using the setInterval(callback,
delayMilliSeconds, [args]) method built into Node.js.
• When you call setInterval(), the callback function is executed every
interval after delayMilliSeconds has expired. For example, the
following executes
28. myFunc() every second:
setInterval(myFunc, 1000);
• The setInterval() function returns a timer object ID. You can pass this
ID to clearInterval(intervalId) at any time before the
delayMilliSeconds expires to cancel the timeout function. For
example:
• myInterval = setInterval(myFunc, 100000);
• … clearInterval(myInterval);
29. Performing Immediate Work with an
Immediate Timer
• Immediate timers are used to perform work on a function as soon as the I/O
event callbacks are executed, but before any timeout or interval events are
executed.
• This allows you to schedule work to be done after the current events in the
event queue are completed.
• Use immediate timers to yield long-running execution segments to other
callbacks to prevent starving the I/O events.
• Immediate timers are created using the setImmediate(callback,[args]) method
built into Node.js.
• When you call setImmediate(), the callback function is placed on the event
queue and popped off once for each iteration through the event queue loop
after I/O events have a chance to be called.
30. • The setImmediate() function returns a timer object ID. You can pass
this ID to clearImmediate(immediateId) at any time before it is picked
up off the event queue. For example:
myImmediate = setImmediate(myFunc);
…
clearImmediate(myImmediate);
31. Dereferencing Timers from the Event Loop
• Often you do not want timer event callbacks to continue to be scheduled
when they are the only events left in the event queue.
• Node.js provides a useful utility to handle this case. The unref() function
available in the object returned by setInterval and setTimeout allows you
to notify the event loop to not continue when these are the only events
on the queue.
• For example, the following dereferences the myInterval interval timer:
• myInterval = setInterval(myFunc);
• myInterval.unref();
• you can use the ref() function to rereference it: myInterval.ref();
32. Using nextTick to Schedule Work
• A useful method of scheduling work on the event queue is the
process.nextTick(callback) function.
• This function schedules work to be run on the next cycle of the event
loop. Unlike the setImmediate() method,nextTick() executes before
the I/O events are fired.
• This can result in starvation of the I/O events, so Node.js limits the
number of nextTick() events that can be executed each cycle through
the event queue by the value of process.maxTickDepth, which
defaults to 1000.
33. Implementing Event Emitters and Listeners
• to implement many of the events built in to the various Node.js modules. This
section focuses on creating your own custom events as well as implementing
listener callbacks that get implemented when an event is emitted.
• Events are emitted using an EventEmitter object. This object is included in the
• events module.
• The emit(eventName, [args]) function triggers the eventName event and
includes any arguments provided.
• The following code snippet shows how to implement a simple event emitter:
var events = require('events');
var emitter = new events.EventEmitter();
emitter.emit("simpleEvent");
35. Adding Event Listeners to Objects
• Once you have an instance of an object that can emit events, you can
add listeners for the events that you care about. Listeners are added to
an EventEmitter object using one of the following functions:
1. .addListener(eventName, callback): Attaches the callback function to
the object’s listeners. Every time the eventName event is triggered,
the callback function is placed in the event queue to be executed.
2. .on(eventName, callback): Same as .addListener().
3. .once(eventName, callback): Only the first time the eventName
event is triggered, the callback function is placed in the event queue
to be executed.
36. Removing Listeners from Objects
• Node.js provides server helper functions on the EventEmitter object
that allow you to manage the listeners that are included. These
include
1. .listeners(eventName): Returns an array of listener functions
attached to the eventName event.
2. .setMaxListeners(n): Triggers a warning if more than n listeners are
added to an EventEmitter object. The default is 10.
3. .removeListener(eventName, callback): Removes the callback
function from the eventName event of the EventEmitter object.
37. I will call back later!“(Call Back Function)
• A callback is a function passed as an argument to another function
• This technique allows a function to call another function
• A callback function can run after another function has finished
38. • A callback function is a function passed into another function as an
argument, which is then invoked inside the outer function to
complete some kind of routine or action.
• Callback functions are functions that are called after the first function
completes its task. They are often used to handle asynchronous
events and make your code more readable.
39. • JavaScript callback functions are important for the following reasons:
• They allow you to handle events. Events in your JavaScript program include things like a user pressing a
button or a network request concluding. Callbacks allow events to be handled as they happen.
• You can use them to send asynchronous API calls. Many APIs are asynchronous, which means they don’t
immediately return a value. They instead give back a promise or a callback method. When the API call is
finished, the callback function is invoked.
• You can use them to enhance your code’s performance. Callbacks enable you to multitask while an
asynchronous action is running, which can help your code execute faster.
• Callback functions can be used to manage the flow of asynchronous operations, preventing the infamous
“callback hell.” This is a situation where deeply nested callbacks make code hard to read and maintain.
• Closures, which allow functions to “remember” the scope in which they were generated, are frequently
used by callback functions. This has the potential to be very effective at preserving state and encapsulating
behavior.
• Higher-order functions, or functions that can take other functions as inputs or return other functions as
values, are based on the concept of callbacks. This functional programming patte
40. • Update the UI after a network request has completed.
• Process data after a file has been read.
• Make another API call after the results of the first API call have been
received.
41. • Node Callback Concept
• A callback in Node is a non-blocking function that executes upon task
completion, enabling asynchronous processing. It facilitates scalability
by allowing Nodejs to handle multiple requests without waiting for
operations to conclude, in file I/O scenarios.
• Explanation: The fs library is used for file-system operations. The
readFileSync() function is synchronous, halting program execution
until completion. This blocking behavior ensures that the program
reads the file before progressing further.
42. Callback Implementation
function exampleAsync(a, b, callback) {
setTimeout(function() {
callback(a + b);
}, 100);
}
console.log('Before asynchronous call’);
exampleAsync(2, 3, function(finalresult)
{
console.log('Result: ' + finalresult);
}); console.log('After asynchronous call')
setTimeout() triggers an asynchronous
process; it will not wait for the callback to get
executed. It returns quickly, providing the
control back to exampleAsync(), and then
back to its caller.
44. const fs = require('fs');
fs.readFile('./1.txt', { encoding: 'utf8', flag: 'r' },
function (err, data1) {
if (err)
console.log(err);
else
console.log(data1);
});
console.log("EXECUTION ENDS");
A callback is a function called when the task finishes,
and a callback function allows other code to run in the
meantime. Using the Callback concept, Node.js can
process many requests without waiting for any
function to return the result, making Node.js highly
scalable. For example: In Node.js, when a function
starts reading the file, it returns the control to the
execution environment immediately to execute the
next instruction. Once file I/O gets completed, the
callback function will get called to avoid blocking or
wait for File I/O.
45. Implementing Closure in Callbacks
• An interesting problem that asynchronous callbacks have is that of
closure.
• Closure is a JavaScript term that indicates that variables are bound to
a function’s scope and not the parent function’s scope.
• When you execute an asynchronous callback, the parent function’s
scope may have changed;
• for example, when iterating through a list and altering values in each
iteration.
46. • If your callback needs access to variables in the parent function’s
scope, then you need to provide closure so that those values are
available when the callback is pulled off the event queue.
• A basic way of doing that is by encapsulating the asynchronous call
inside a function block and passing in the variables that are needed.
47. Fs module(const fs = require('fs');)
• Use fs.readFileSync() :
• When you are working with small files, such as configuration or startup files, where
blocking the execution won’t impact performance.
• In scenarios where synchronous execution is required, such as in scripts that run before
the event loop starts.
• When working with single-threaded, short-lived programs, like command-line utilities.
• Use fs.readFile() :
• When performance and scalability are critical, such as in web servers handling multiple
requests.
• For non-blocking, event-driven architectures where I/O operations should not block the
event loop.
• In production environments where multiple file reads can happen concurrently, and
blocking could result in significant performance degradation