Event Driven Programming and Node JS Quick Review
Event-driven programming is a programming paradigm in which the flow of the program is determined by events such as user actions (mouse clicks, key presses), sensor outputs, or messages from other programs/threads. In practice, it means that applications act on events.
NodeJS Thread Model
- NodeJS is single-threaded.
- Event Loop provides NodeJS the event-driven programming.
- Event Loop schedule tasks(/events)
- Each time an event occurs, it is placed in the Node’s event queue
- On each iteration of the event loop, a single event is dequeued and processed.
- If during the process, the event creates any additional events, they are simply added to the end of the queue along with a callback
- NodeJS uses callbacks to avoid waiting for blocking I/O.
- When the event is executed, control is returned to the event loop and another event is processed.
- When all events on NodeJS event queue are executed, NodeJS terminates the application.
Building blocks of NodeJS:
- Reactor Pattern
- Set of Bindings
- Core JS Library
I/O is Slow: I/O is the fundamental operation of a computer and it is slow. Accessing the RAM is a matter of nanoseconds while accessing the data on disk or network is a matter starts in milliseconds.
Blocking I/O: Traditionally, a system calls an I/O request that comes to a webserver and is assigned to an available thread or each concurrent connection there is a thread. The request is handled continuously on that thread until the request is complete and response is sent. While handling the data between functions such as GetFile(file) & Open(File) there will be some amount of idle time. Thread consumes memory, so a long-running thread for each connection & not using it is not efficient.
Traditional Non-Blocking I/O: A system call is returned immediately without waiting for the data to be read or written (aka operation to complete). If no results are available at the moment of the call, the function will simply return a predefined constant, indicating that there is no data available to return at that moment. A loop iterate over the resource and when the resource is found (the operation is completed) it is returned. This loop consumes CPU for iterating over the resource that is unavailable most of the time. This is called busy-waiting. Traditional Non-Blocking I/O.
- It’s a modern way of Non-Blocking I/O.
- For efficient Non-Blocking I/O there is synchronous event-demultiplexer (aka:event notification interface)
- It collects and queues I/O events that come from requests and block (sits idly ) until new events are available to process.
- Each event is associated with a specific operation.
- Event notifier is set on the group of resources to be watched.
- This call is synchronous and blocks(idle time) until any of the watched resources is ready for a read.
- Event Demultiplexer returns from watched resources when they are being processed and a new set of events are available to be processed.
- Each event returned by the event demultiplexer is processed.
- When all the events are processed, the flow will block again(there will be idle time in between) on the event demultiplexer until new events are again available to be processed. This is called the event loop.
- This way several I/O operations can be handled in a single thread.
- minimalize idle time
- having a single thread handle multiple requests
- Reactor Pattern have a handler(in case of Nodejs, have a callback function) associated with each I/O operation.
- This handler/callback function is invoked when event is produced & processed by Event Loop.
What happens in an Application using the Reactor Pattern?
- Application generates I/O request by submitting request to Event Demultiplexer
- The application also specifies handler/callback function which will be invoked when operation completes
- Submitting a new request to the Event Demultiplexer is a non-blocking call and it immediately returns the control back to the application.
- When I/O operation completes, the Event Demultiplexer pushes the new events into the Event Queue.
- At this point, the Event Loop iterates over the items of the Event Queue.
- For each event, the associated callback function is invoked.
- callback function will give control to the event loop when its execution completes.
- New asynchronous operations might be requested during the execution of the callback function, causing new operations to be inserted in the Event Demultiplexer
- When all items in Event Queue are processed, and there no pending operations in Event Demultiplexer, Node.js application will exit automatically.
libuv - Non-Blocking I/O Engine
- It’s pronounced as “lib u v”
- Each operating system has its own interface for the Event Demultiplexer
- eg: epoll on Linux, kqueue on Mac OS X & IOCP on Windows
- Each I/O operation can behave quite differently depending on the type of the request, even within the same OS.
- This creates inconsistency
- To overcome this inconsistency, Nodejs Core team built libuv
- libuv is a C library, created to make Node.js compatible with every OS & normalize the non-blocking behavior of the different OS types.
- libuv is a low-level I/O Engine
- it implements Reactor Pattern, providing an API for creating even loop, managing event queue, running asynchronous I/O operation.
Set of Bindings
- Compiles and executes JS
- Developed by Google for Chrome browser
- Reason why Nodejs is fast
- V8 is acclaimed for its revolutionary design, its speed, and for its efficient memory management.
Core JS library