node js - what happens to incoming events during callback excution

They are added to queue and processed later:

A JavaScript runtime contains a message queue, which is a list of messages to be processed. A function is associated with each message. When the stack is empty, a message is taken out of the queue and processed. The processing consists of calling the associated function (and thus creating an initial stack frame). The message processing ends when the stack becomes empty again.

Concurrency model and Event Loop


The event loop does not poll. Therefore not being able to process the event loop does not affect incoming events.

How the event loop work:

Most modern OSes (or unix-like ancient OSes) handle I/O at the OS level instead of the application level. The POSIX standard requires the OS to support at least the select() system call. The select() function is a blocking function that most programs use to handle non-blocking I/O. That statement sounds contradictory but it's not.

How non-blocking I/O work:

I'm going to use select() as an example but different OSes also have other non-blocking API like poll() and epoll() and overlapped-IO (Windows). Various javascript engines typically use a library like libuv to automatically handle which API to use at compile time.

A non-blocking API typically provides one function like select() that blocks and waits for events on any I/O the application is listening on. Why blocking? Because that's the only way for the program to use 0% CPU time. Otherwise the process will be busy polling and that would be very inefficient.

Side note: What does blocking mean? Blocking is basically any function that tells the OS: hey, I'm waiting for this "thing" so can you remove me from the CPU sharing schedule and wake me up only when the "thing" arrive?

The difference between non-blocking I/O and blocking I/O is not that you never block, non-blocking I/O blocks waiting on multiple I/O whereas blocking I/O blocks waiting on a single I/O. If you want to know more google the documentation of the select() POSIX function.

Anyway, javascript uses non-blocking I/O so it does not block on reading from I/O but blocks on select() or similar functions. When the interpreter is executing javascript code obviously it is not simultaneously calling the select() function. So while the interpreter is busy the OS buffers any I/O destined for the program.

Does the OS poll?

No. The OS generally does not poll (then again it depends on the device driver but in general no). I/O activity is handled by interrupts. Even for non-interrupt driven I/O (for example USB) generally the chipset that handles that I/O will generate an interrupt when its buffers are full so that the OS will copy the data to OS buffers in RAM. Sometimes for high speed devices it's not even the OS that does the copy but the DMA controller which would generate an interrupt once data is copied to RAM.

What about GUI activity?

In the end, GUI activity like mouse clicks and key presses are also interrupt driven (early version of DOS based GUI managers like Windows 1.0 used poll driven mouse driver, then Microsoft saw a demo of the Mac OS and legend has it that an engineer at Apple let slip that they didn't poll, since then mouse drivers generally trigger interrupts).

The exception:

One minor exception is threads in javascript. By threads I mean web workers in browsers and disk I/O handlers in node.js. In node.js for example disk I/O drivers are implemented as blocking I/O in individual threads. So node.js is responsible for buffering data before passing it back to the event loop. Again, all the OS buffering layers still exist: while copying data for example the OS may buffer a completed disk read command before the node.js thread call the next read(). In any case, the threads still communicate with the event loop via I/O channels, either pipes or sockets or unix domain sockets so everything I outlined above still holds: if the main js thread is busy the OS will simply buffer data from the threads (or if it's blocking then the threads will simply block until the event loop process their I/O).


They're queued and handled in order upon being pulled from the event queue.

Your JS code can't block new events from entering the queue.