Table of contents
Open Table of contents
Context
Applications/frameworks that use event loop include Node.js, Netty, and Jetty.
console.log('first')
fs.readFile(__filename, ()=> {console.log('second')}) // async, callback executed last
console.log('third')
Event Loop
The event loop is a key concept in asynchronous programming and is used to handle and manage events and tasks in a program. Its origins trace back to systems programming and were popularized by the need for efficient input/output operations in software.
First Usage of Event Loops
Historical Context:
- The concept of an event loop was first seen in early operating systems where the system needed to wait for and respond to hardware interrupts or events (e.g., key presses or I/O signals).
- Early graphical user interfaces (GUIs), such as those in systems like X Window System and Microsoft Windows applications, employed event loops to manage user interactions like clicks and drags.
Programming Paradigms:
- Event loops gained prominence in event-driven programming, especially in the design of GUIs and networked applications.
- In Node.js, the event loop is a core part of its runtime, enabling it to perform non-blocking I/O operations. This implementation is based on the
libuv
library.
Multiple queues:
Language-Specific Implementations:
- In JavaScript, the event loop is used to handle the execution of asynchronous code (e.g.,
promise
,setTimeout
), often in conjunction with the browser or Node.js runtime. - In Python, the event loop is managed by frameworks like asyncio, introduced in Python 3.4.
Networking:
- Early networking systems also used event loops, particularly in single-threaded network servers like nginx or early implementations of
select
orpoll
system calls in UNIX.
function main
initialize()
while message != quit
message := get_next_message()
process_message(message)
end while
end function
The get_next_message()
routine is typically provided by the operating system and blocks until a message is available.
The loop is only entered when there is something to process.
Under Unix, the “everything is a file” paradigm naturally leads to a file-based event loop. Reading from and writing to files, inter-process communication, network communication, and device control are all achieved using file I/O, with the target identified by a file descriptor. The select and poll system calls allow a set of file descriptors to be monitored for a change of state, e.g., when data becomes available to be read.
NodeJs event loop has six phases (see nodejs doc link in references):
- timers: this phase executes callbacks scheduled by
setTimeout()
andsetInterval()
. - pending callbacks: executes I/O callbacks deferred to the next loop iteration.
- idle, prepare: only used internally.
- poll: retrieve new I/O events; execute I/O related callbacks (almost all with the exception of close callbacks, the ones scheduled by timers, and
setImmediate()
); node will block here when appropriate. - check:
setImmediate()
callbacks are invoked here. - close callbacks: some close callbacks, e.g.
socket.on('close', ...)
.
Actor Model vs. Reactor Pattern
They both use event loops.
Actor model (wiki) originated in 1973. It adopts the philosophy that everything is an actor. Thi is similar to the everything is an object philosophy in Python.
Email, web services, and object with locks can be modeled as an actor system.
An actor is a computational entity that, in response to a message it receives, can concurrently:
- send a finite number of messages to other actors;
- create a finite number of new actors;
- designate the behavior to be used for the next message it receives.
There is no assumed sequence to the above actions, and they could be carried out in parallel.
Erlang, Ruby, Scala, and Swift employ the actor model.
Libraries and frameworks include Actix (Rust), Actor (Java), Vert.x (Java), and Pulsar (Python).
Difference between the two discussions on stackoverflow.
In pulsar, each actor (think of a specialized thread or process) has its own event loop. In this way, any actor can run its own asynchronous server.
The reactor pattern is used in many web servers, application servers, and networking frameworks include Netty, Nginx, Node.js, Twisted, and Vert.x.