Is setInterval blocking?

5.6k views Asked by At

I have the following Node application

var express = require("express"),
    app = express();


app.get("/api/time", function(req, res) {
    sendSSE(req, res);
});

function sendSSE(req, res) {
    res.set({
        "Content-Type": "text/event-stream",
        "Cache-Control": "no-cache",
        "Connection": "keep-alive",
        "Access-Control-Allow-Origin": "*"
    });

    var id = (new Date()).toLocaleTimeString();

    setInterval(function() {
        constructSSE(res, id, (new Date()).toLocaleTimeString());
    }, 5000);

    constructSSE(res, id, (new Date()).toLocaleTimeString());
};


function constructSSE(res, id, data) {
    res.write("id: " + id + "\n");
    res.write("data: " + data + "\n\n");
}

var server = app.listen(8081, function() {

});

I am using it to use Server Side Events with my client app. When I browse to http://localhost:8081/api/time it starts returning straight away. If I open the URI in another browser window then it will take several seconds before it responds, however then it works fine.

So my question is setInterval blocking, or is there some other explanation for the poor performance? Based on this answer it is not supposed to be, but I would not expect that constructSSE would take 5 seconds. However, I am seeing an issue.

Thanks.

Update As suggested that it might be something to do with express, I removed it and just used the http module.

var http = require("http");

http.createServer(function(req, res){
    if (req.url == "/api/time") {
        sendSSE(req, res);
    }
}).listen(8081);

function sendSSE(req, res) {
    res.writeHead(200, {
        "Content-Type": "text/event-stream",
        "Cache-Control": "no-cache",
        "Connection": "keep-alive",
        "Access-Control-Allow-Origin": "*"
    });

    var id = (new Date()).toLocaleTimeString();

    setInterval(function() {
        constructSSE(res, id, (new Date()).toLocaleTimeString());
    }, 5000);

    constructSSE(res, id, (new Date()).toLocaleTimeString());
};


function constructSSE(res, id, data) {
    res.write("id: " + id + "\n");
    res.write("data: " + data + "\n\n");
};

It has the exact same behaviour. So it looks like some limitation of Node, or a mistake by me.

1

There are 1 answers

2
devconcept On BEST ANSWER

I think the reason of the poor performance is not related to node or setInterval itself but the way you read your event data. I do a little search and implement 3 or 4 different examples found on the web of Server Sent Events on node and they all suffer from the same problem.

Thinking that node.js was no good for the task didn't fit on my head.

I tried

  1. Using process.nextTick
  2. Increasing the interval
  3. Using setTimeout instead of setInterval
  4. With express.js or just node.js
  5. With some node modules like connect-sse and sse-stream

I was about to start testing using webworker-threads or the cluster module or another server platform trying to pin down the problem and then I had the idea to write a little page to grab the events using the EventSource API. When i did this everything worked just fine, moreover in the previous tests using Chrome i see that in the Network Tab the SSE request contain an additional tab called EventStream as was espected but the content was empty even when data was arriving regularly.

This lead me to believe that maybe it was the browser not behaving correctly interpreting the request in the wrong way due it was requested using the address bar and not the EventSource API. The reasons for this I don't know but I made a little example and there is absolutely no poor performance.

I changed your code like this.

  1. Added another route to the root of website for testing

    var express = require("express"),
    app = express();
    
    // Send an html file for testing
    app.get("/", function (req, res, next) {
        res.setHeader('content-type', 'text/html')
        fs.readFile('./index.html', function(err, data) {
            res.send(data);
        });
    });
    
    app.get("/api/time", function(req, res) {
        sendSSE(req, res);
    });
    
  2. Created an index.html file

    <head>
        <title>Server-Sent Events Demo</title>
        <meta charset="UTF-8" />
        <script>
            document.addEventListener("DOMContentLoaded", function () {
                var es = new EventSource('/api/time'),
                    closed = false,
                    ul = document.querySelector('ul');
    
                es.onmessage = function (ev) {
                    var li;
                    if (closed) return;
    
                    li = document.createElement("li");
                    li.innerHTML = "message: " + ev.data;
                    ul.appendChild(li);
                };
    
                es.addEventListener('end', function () {
                    es.close()
                    closed = true
                }, true);
    
                es.onerror = function (e) {
                    closed = true
                };
            }, false);
        </script>
    </head>
    <body>
        <ul>
        </ul>
    </body> 
    

{Edit}

I also want to point out, thanks to @Rodrigo Medeiros, that making a request to /api/time with curl shows no poor performance which reinforce the idea that is is a browser related issue.