Long polling in Node.js

Note: Node is currently under heavy development and the API is in a state of flux. This article was written to be compatible with v0.1.95, the most recent version of Node at the time. The examples in this article may not work with future versions.

Many web developers seem to think there is some sort of black magic behind long polling, either because the language or web server they use doesn’t support it or because they correlate it with server push technology. In this article, we’ll show how to implement long polling in Node.js and explain why you don’t need to do anything special on the client to support it.

Request types

We’re all familiar with standard web requests: the client requests a resource, the server responds synchronously, then the page is displayed. We’re also familiar with the concept of ajax requests in which the client requests a resource asynchronously and is notified when the server responds. Typically the server doesn’t treat ajax requests asynchronously; a request comes in and the server immediately (and synchronously) processes the request and responds. The asynchronous part just happens on the client where the browser doesn’t block while it waits for the server to respond.

Long polling is an ajax request in which the request and response are both treated as asynchronous operations. Rather than accepting the request and immediately processing to return a result, the server accepts the request and holds on to it until some other event occurs, providing data to send as a response to the original request. Most scripting languages don’t support holding on to requests for long periods of time because the script runs from top to bottom and then immediately responds. Trying to hold on to requests in these languages would require creating a blocking loop that waits for the relevant event to occur and that would quickly kill your server as requests build up. Node scripts run in an event loop, making them ideal for holding on to requests without blocking other processes.

Handling requests in Node

Because Node is specifically built to provide non-blocking I/O, even the Hello World example from the docs shows off an asynchronous response to an HTTP request.

var http = require("http");
http.createServer(function(request, response) {
	setTimeout(function() {
		response.writeHead(200, { "Content-Type": "text/plain" });
		response.end("Hello, World!");
	}, 2000);
}).listen(8000);

When a request comes in, the server will wait two seconds and then respond with “Hello, World!”. The important thing in this example is that the script is not blocking while it waits two seconds. This means that other requests can come in during that two second period and be handled immediately. In this case, those additional requests would also wait for two seconds before getting a response, but the response would come two seconds after the request was initiated, not two seconds after the previous request is closed.

Let’s split the response generation out of the main function to get a better idea of what’s really going on here.

var http = require("http");
var requests = [];

http.createServer(function(request, response) {
	// store the response so we can respond later
	requests.push(response);
}).listen(8000);

setInterval(function() {
	// respond to each request
	while (requests.length) {
		response = requests.shift();
		response.writeHead(200, { "Content-Type": "text/plain" });
		response.end("Hello, World!");
	}
}, 2000);

There are a few differences between this example and the first example. Right away, we can see that when a request comes in we don’t make any attempt to respond to it, we just throw the response object into an array and seemingly ignore it. Next we have a function that runs every two seconds and responds to all the requests that we’re holding on to. Since this interval is based on when the script was started, the request may be held for anywhere between one millisecond and two seconds, depending on how close the request comes in to the next run of the interval. This better represents the standard model for a long polling implementation in which some event not directly related to the request occurs on the server and triggers a response.

A more complex example

Let’s build a script the pipes stdin to HTTP requests. When an HTTP request comes, we’ll check to see if we have any data already stored from stdin. If we do, we’ll just respond to the request immediately; if we don’t, we’ll hold on to the request and respond whenever we detect some action from stdin. Likewise, when we receive data from stdin, we’ll check to see if we have a pending HTTP request. If we do, we’ll respond to the request with the data that just came in; if we don’t, we’ll store the data and respond whenever a request comes in.

var http = require("http");
var pendingResponse;
var chunks = "";

http.createServer(function(request, response) {
	// if we have data, send it
	if (chunks.length) {
		response.writeHead(200, { "Content-Type": "text/plain" });
		response.end(chunks);
		chunks = "";

	// no data sitting around, store the response for later
	} else {
		pendingResponse = response;
	}
}).listen(8000);

process.openStdin().addListener("data", function(chunk) {
	// if we have a pending request, send the data
	if (pendingResponse) {
		pendingResponse.writeHead(200, { "Content-Type": "text/plain" });
		pendingResponse.end(chunk);
		pendingResponse = null;

	// no pending request, store the chunk from stdin for later
	} else {
		chunks += chunk;
	}
});

Preventing timeouts

One problem you might run into with long polling is holding on to the request too long. If you take too long to respond, the request will eventually time out. To prevent this from occurring, you can keep track of when the requests come in, and send an empty response if the request is still open after a certain period of time. When the client receives the empty response, it will start the next long poll request. This can easily be accomplished with a slight tweak to the code above that uses an interval to respond to requests. Instead of just responding to all requests, we would check if the request has been pending for too long, and close it out if it has.

var http = require("http");
var requests = [];

http.createServer(function(request, response) {
	// store the response so we can respond later
	requests.push({
		response: response,
		timestamp: new Date().getTime()
	});
}).listen(8000);

setInterval(function() {
	// close out requests older than 30 seconds
	var expiration = new Date().getTime() - 30000;
	var response;
	for (var i = requests.length - 1; i >= 0; i--) {
		if (requests[i].timestamp < expiration) {
			response = requests[i].response;
			response.writeHead(200, { "Content-Type": "text/plain" });
			response.end("");
		}
	}
}, 1000);

Long polling on the client

As mentioned above, there’s nothing special that needs to be done on the client for long polling. The general concept is just to make an ajax request and when you get a response, handle it and make the ajax request again. This process just loops over and over, handling data whenever the server responds.