"Why does simple "Hello world"-program exit immediately after printing to console, but starting an HTTP server keeps running forever?"
Consider the classic "Hello world!"-program. It prints short text to the screen and then exits. The control is immediately returned to the originating shell.
However, starting an HTTP server behaves differently. It will not return to console but keeps on running indefinitely.
Why does this happen?
Node.js keeps track of active event listeners
At the heart of Node, events and event listeners are what make up an application. A program can start a file read operation, and every time there's more data to be read, a 'data' event gets emitted. When there's an incoming HTTP request on a server, a 'request' event gets emitted.
An event listener is a piece of code that's to be executed when the event takes place. Such event listeners can be, for example:
a timer waiting for system clock to reach certain reading
a handler to be invoked whenever new HTTP request arrives
a code that's waiting for file read to complete
Deep in its internals, Node.js keeps track of registered event listeners.
Program lives as long as there are active event listeners
A Node.js program will keep running as long as there are active event listeners. After the last event listener has finished or otherwise terminated, the Node.js program will also terminate.
This is exactly what happens in the example. Node.js reads the source for the Hello world program and executes it. The program prints to the console. When it reaches the end, there are no active event listeners left, and the program exits. In fact, the program is so simple that there never were any event listeners at all.
However, the HTTP server behaves differently. The program creates an HTTP server and binds it to a port. By doing that, it registers a handler to be invoked whenever a new HTTP connection is established. At the point where the program code reaches the end, there is exactly one event listener active. The program will not terminate but keeps on running instead.
You could terminate the server program by issuing server.close() after a while. It would then finish serving any pending connections and eventually exit. At that point, it would have no active event listeners present.