Diving into Node.js – A Long Polling Example

Node.js vs. The World

What is typical for most of the web servers is that they listen for requests and respond as quickly as possible on every one of them. In fact the speed of the response is one of the main targets of optimization for developers. Fast servers are what everyone needs. From web developers to website visitors!

In the field of the that battle different web servers have different “weapons” to gain time. While this is useful in most of the cases, when it comes to a chat-like applications and Node.js approaches, the response is not always immediately returned. As I described in my posts until now about Node.js, a simple web server may wait for an event to be emitted, and than return the response.

I wrote about how to write the very first server, but than I didn’t described how to make a “non-responding” server. By the term “non-responding” I mean a server that responds not immediately after it has received and parsed/executed the request.

A typical web server, responding immediately on every request, written with Node, this may look like so:

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(8124, "127.0.0.1");
console.log('Server running at http://127.0.0.1:8124/');

The code that shows us that the request is executed and responds in these lines:

  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');

Actually by commenting/removing those lines you’ll get a server that simply doesn’t respond – ever! This of course is not the main goal, but you can start from somewhere.

var http = require('http');
http.createServer(function (req, res) {
  // res.writeHead(200, {'Content-Type': 'text/plain'});
  // res.end('Hello World\n');
}).listen(8124, "127.0.0.1");
console.log('Server running at http://127.0.0.1:8124/');

This is how you can hold for a while. Perhaps putting these lines in a conditional statement and periodically checking for some event to occur will do the job.

var http = require('http');
http.createServer(function (req, res) {
  if (something) {
     res.writeHead(200, {'Content-Type': 'text/plain'});
     res.end('Hello World\n');
  }
}).listen(8124, "127.0.0.1");
console.log('Server running at http://127.0.0.1:8124/');

Looping Server

As described in the code above if you put the respond into a conditional you can possibly return the response after some event has fired. The only thing is to periodically loop and check for this condition to be true. Assuming your server’s code is in server.js, started with the “node server.js” command, you’ve to put this code into it:

var http = require("http"),
    fs	 = require("fs");  
 
// we create a server with automatically binding
// to a server request listener
http.createServer(function(request, response) {
	checkFile(request, response);
}).listen(8124);
 
function checkFile(request, response)
{
	var date = new Date();
	if (date-request.socket._idleStart.getTime() > 59999) {
		response.writeHead(200, {
			'Content-Type'   : 'text/plain',
			'Access-Control-Allow-Origin' : '*'
		});
 
		// return response
		response.write('OK', 'utf8');
		response.end();
	}
 
	// we check the information from the file, especially
	// to know when it was changed for the last time
	fs.stat('filepath', function(err, stats) {
		// if the file is changed
		if (stats.mtime.getTime() > request.socket._idleStart.getTime()) {
			// read it
			fs.readFile('filepath', 'utf8', function(err, data) {
				// return the contents
				response.writeHead(200, {
					'Content-Type'   : 'text/plain',
					'Access-Control-Allow-Origin' : '*'
				});
 
				// return response
				response.write(data, 'utf8');
				response.end();
 
				// return
				return false;
			});
		}
	});	
 
	setTimeout(function() { checkFile(request, response) }, 10000);
};

Note: you must change “filepath” with an existing file path in your system.

Here we can read periodically the file’s mtime, which is the modify time. Thus whenever the file is changed the server will return the response. It’s interesting to note that if no change occurs within the timeout period you’ve to return some status message as we did in those lines:

var date = new Date();
if (date-request.socket._idleStart.getTime() > 59999) {
	response.writeHead(200, {
		'Content-Type'   : 'text/plain',
		'Access-Control-Allow-Origin' : '*'
	});
 
	// return response
	response.write('OK', 'utf8');
	response.end();
}

The client side should long poll this server. The example bellow is written in jQuery. The only “special” thing is that after receiving the response, you must call the server again.

The Client

The client is nothing new to the jQuery/JavaScript community except you must call again the server when the response is returned:

function callNode() {
    $.ajax({
        cache : false,
		// setup the server address
        url : 'http://www.example.com:8124/',
        data : {},
        success : function(response, code, xhr) {
 
            if ('OK' == response) {
            	callNode();
                return false;
            }
 
            // do whatever you want with the response
            ...
 
            // make new call
            callNode();
        }
    });
};
callNode();

Summary

As Node can hold the response, the only thing you should do is to check periodically for something as in this example this was a file change. Than it’s important to change the client so it can call the server after the response from it is received.

Typically Node can be used for chat-like applications or whatever apps that must deliver real time data, but for sure it is really great software.

19 thoughts on “Diving into Node.js – A Long Polling Example

  1. Hi,

    i’m totally new to node-js (not to javascript..) so i’m probably wrong, but i’m a bit confused: with ‘still fresh’ copies of documentation and presentation-video’s in the back of my head, implementing a polling-loop like you do here, seems to go completely against the whole event-driven philosophy of node-js.

    why not install an event-listener to get notified by the system whenever the file is opened/changed ?

  2. Great article. I did get a memory leak and traced it down to missing a return value in the “OK” response stanza.

    // return response
    response.write(‘OK’, ‘utf8’);
    response.end();
    return false;

    Cheers

  3. Hi, very useful post. Hoever the example code won’t work in IE8 and IE7. Maybe IE doen’t understand access-control-origin ?

  4. Thanks. This is a great article!

    @arri: This example is to demonstrate the “long polling” concept, which includes “delayed response to caller”. The fs.stat/fs.readFile calls infact queues the result to an event-handler. So, am not clear on what you mean by “install an event-listener”. Could you please clarify ?

    Thanks.

  5. Thanks for sharing. As my understanding, we are broadcasting to all connected client when an even fired. How can I specify users got notified. For example, user A send user B a message, I just want only user B got notified. Do you have any idea?
    Thank you in advance.

  6. really nice illustration of the long polling concept in a node.js context….
    However, I’m wondering if calling recursively the callNode() function is a good idea (no, seriously, I really can’t imagine side effects)

    wouldn’t it be nicer to setTimeout(callNode, 1), just to avoid growing the callstack client side?

  7. @wiill I agree, the result would be the same, but way safer.
    In fact a setTimeout calling the function where it is contained is a commonly adopted safe way to do cycle indefinetly with invervals, even instead of setInterval, because it makes sure that the calling function ends before the called one starts.

  8. Thank you for sharing such a good tutorial.

    Everything was looking so good unless you jumped to a massive example in ‘Looping Server’ section and that made me really sad. You should have add simpler examples before reading to this complex example step by step so we can try your code while reading it :/

    Googling again hopelessly to find a more simpler tutorial 🙁

  9. I put this above ‘Looping Server’ code in server.js.

    But there is no response. am using ubuntu.

    Any solution?

  10. Great post, but I’m getting the following error when the client makes a request (using node v0.12.0

    if (date-request.socket._idleStart.getTime() > 59999) {
    ^
    TypeError: undefined is not a function

    I can’t find any documentation on ‘date-request’, and it looks like you are using the same thing down in the ‘if file has changed’ block, but are not including ‘date-request’.

    Any idea what I am doing wrong here? I copied and pasted your code and it’s throwing this error.

  11. In the long poll resolution when you timeout, instead of responding status=200 with an arbitrary “OK” response, respond status=204 with no data.

  12. @Mehr
    Node has depreciated the use of the _idleStart property of sockets.
    This could be re-implemented without the use of that function by simply passing around an object contains the time.

    A version using this fix is availble here.
    https://pste.me/#/fix-for-long-polling-example.js

    I also added some returns and a clearTimeout to avoid callbacks firing on dead sockets

  13. ome problem with it is that node js will block others request if u do nor res,end() the current connection. if u start spawning workers for each of the long poll request that can take quite a while u will end up having apache server instead of even based server.

Leave a Reply

Your email address will not be published. Required fields are marked *