I am familiar with event based systems in c++ as well as java. I was trying to learn node.js and ran into interesting behavior and I was hoping someone can explain what is going on under the hood.
I have a program that looks like
var http = require("http");
function main(){
// Console will print the message
console.log('Server running at http://127.0.0.1:8080/');
var server = http.createServer(function (request, response) {
// Send the HTTP header
// HTTP Status: 200 : OK
// Content Type: text/plain
response.writeHead(200, {'Content-Type': 'text/plain'});
// Send the response body as "Hello World"
response.end('Hello World\n');
});
server.listen(8080); //Why is this not blocking
console.log('Main completed');
//main loop here prevents other stuff from working
}
main();
In languages like java or c I would expect two things. Either server.listen provides an event loop which would cause server.listen to never return. Or server.listen spawns a new thread and runs the event loop in the new thread returning right away. Then it would call console.log and then return and close the program.
To test this I also added a busy loop below the console.log that looks like.
var http = require("http");
function main(){
// Console will print the message
console.log('Server running at http://127.0.0.1:8080/');
var server = http.createServer(function (request, response) {
// Send the HTTP header
// HTTP Status: 200 : OK
// Content Type: text/plain
response.writeHead(200, {'Content-Type': 'text/plain'});
// Send the response body as "Hello World"
response.end('Hello World\n');
});
server.listen(8080); //Why is this not blocking
console.log('Main completed');
while(true){
console.log('hi');
}
}
main();
Server.listen returns right away and then gets stuck in the busy loop as expected. My browser can not connect to the server, which is expected.
If I remove the busy loop and go back to the original code once console.log('Main completed'); is executed instead of the program exiting, the main thread jumps back into the event loop.
How does this work. Why does the main thread jump back into the server code after the main thread returns?
Edit: I think re resolved that the event queue does not exist within the main function but where is it? What owns it? and when does the main function get run in reference to it?
Think of http.createServer(handler)
and server.listen(port)
as being kinda similar to someElement.addEventListener('click', handler)
in the browser.
When you run someElement.addEventListener('click', handler)
, it binds an event listener that will send handler
to the callback queue when the click event is triggered on someElement
.
http.createServer(handler)
combined with server.listen(port)
work in a very similar way. http.createServer(handler)
returns an eventEmitter
, and server.listen(port)
tells node.js that when an http request is received on the given port, this event should be triggered. Thus, when a request comes in, the event is triggered, and handler
is pushed to the callback queue.
At this point, it's just a simple matter of how the event loop callstack and callback queue interact. The callstack is the currently executing stack of functions, the callback queue is a collection of callbacks waiting to be executed, and the event loop is what pulls callbacks off of the callback queue and sends them to the callstack.
So, in a sense, the event loop is what keeps everything going, however, if you block the callstack from emptying by having a synchronous infinite loop in one of your callbacks, the event loop never runs again and callbacks never get executed resulting in a broken/unresponsive application.
The fundamental concept to understand here is the event loop and the cooperative (non-preemptive) multitasking model.
That server.listen
call registers a callback with the event loop underneath, and that callback shares the main execution thread with every other registered callback.
When you throw that busy loop in, the server.listen
callback never gets execution time (and neither does the event engine, for that matter), because the system is non-preemptive. That is, no callback can interrupt any other - callbacks must relinquish control back to the event loop by terminating, whereupon the event loop will call other registered callbacks based on events it has queued.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With