Search code examples
javascriptnode.jshttpserver

Why does a NodeJS http server not time out in an infinite loop?


I set the NodeJS http server timeout to 10 seconds:

const httpServer = require('http').createServer(app);
httpServer.timeout = 10 * 1000;

An incoming request to the server times out after 10s as expected when running a 20s timeout:

await new Promise(resolve => setTimeout(resolve, 20000));

The server also times out as expected when a backend request takes 20s, e.g. to:

http://httpstat.us/200?sleep=20000

But it does not time out when running an infinite loop:

while(true) {}

Why is that?


Solution

  • When you're running an infinite loop, NO other events in Javascript can run. The event loop is blocked. So, not even the timer that controls the timeout can run. This is a consequence of the fact that Javascript runs your JS in a single thread and things like setTimeout() are event driven, not pre-emptive. So, if you're in an infinite loop, then the event loop can't process any events and the setTimeout() never gets to fire that would normally cause the http timeout.

    Welcome to the event-driven architecture of node.js.

    And, just in case you were wondering, this:

    while(true) {}
    

    stops all other activity in node.js too, not just timers. The only thing that could run is worker threads, but they couldn't even communicate with the main thread with the event loop blocked.

    FYI, you can demo this for yourself with something like this:

    let start = Date.now();
    
    function time() {
        let delta = (Date.now() - start) / 1000;
        return delta.toFixed(3);
    }
    
    console.log(`${time()}: Starting 500ms timer`)
    
    // set timer for 500ms
    setTimeout(() => {
       console.log(`${time()}: timer callback called`);
    }, 500);
    
    // spin for 3 seconds
    while (Date.now() - start < 3000) {}
    
    console.log(`${time()}: finished while loop`);

    You can very clearly see that the timer can't fire until the while loop ends. While the timer is supposed to fire in 500ms, it doesn't get to process its callback until after the 3 second while loop is done.