Long polling with Node.js and ajax

12,766

I have figured this out. Amended code that work is as below:

Client side:

<html>
<head>
    <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
    <title>Node.js Ajax test</title>
</head>
<body>

</body>
<script>
var timestamp;
function callNode() {

    $.ajax({
        url: 'http://xx.xxx.xx.xxx:9000/',
        dataType: "jsonp",
        data: {"timestamp":timestamp},
        //type: 'POST', //don't need this with jsonp
        jsonpCallback: "_testcb",
        cache: false,
        timeout: 35000,
        success: function(response, code, xhr) {
            if ('ok' == response) {
                console.log(response);
                callNode();
                return false;
            }

            console.log(response);

            timestamp = response.time;
            // make new call
            callNode();
        },
        error: function(jqXHR, textStatus, errorThrown) {
            console.log('error ' + textStatus + " " + errorThrown);
        }
    });
}
$(function () {
    setTimeout(callNode, 1); //call function with setTimeout in order to avoid ugly constant browser loading 
});
</script>
</html>

Server Side (server.js):

var http = require('http');
var mysql = require('mysql');
var util = require('util');
var url = require('url');

var singer_name, currentmodif, lastmodif, request, response, time_of_request;

//var requests=[];

var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'someone',
  password : 'xxxxxx',
  database : 'rest',  //mysql database to work with (optional)
});
connection.connect(); //connect to mysql

connection.query('SELECT * FROM musics WHERE id=1', function(err, rows, fields) {
  if (err) throw err;

  singer_name=rows[0].singer_name;
  currentmodif=rows[0].time_added;
});


http.createServer(function (req, res) {
    request = req;
    response = res;
    time_of_request = new Date().getTime();
    console.log('request received');


    if(req.method=='GET'){
        var url_parts = url.parse(req.url,true);
        lastmodif = url_parts.query.timestamp;
    }

    req.on('error', function(e) {
        console.log('problem with request: ' + e.message);
    });

    //checkupdate();     

}).listen(9000);

var response;

function checkupdate() { 

    var expiration = new Date().getTime() - 30000;

    //for (var i = requests.length - 1; i >= 0; i--) {
        //console.log("Request timestamp: "+time_of_request+" Expiration : "+expiration);
        if (time_of_request < expiration) {
            console.log("The condition is met");
            // return response
            response.write('_testcb(\'ok\')', 'utf8');
            response.end();
        }
    //}

    connection.query('SELECT * FROM musics WHERE id=1', function(err, rows, fields) {
        if (err) throw err;
        currentmodif=rows[0].time_added;

        if (lastmodif == undefined)
            lastmodif = 0;

        console.log("currentmodif: "+currentmodif+" lastmodif: "+lastmodif);

        if (currentmodif > lastmodif){
            singer_name=rows[0].singer_name;  
            var _arrays = {'singer_name': singer_name, 'time': currentmodif} 
            var data = "_testcb"+"("+JSON.stringify(_arrays)+")";

            //response.writeHead(200, { 'content-type':'application/json',
                                    //'Access-Control-Allow-Origin' : '*'});
            //response.write(data); 
            response.end(data);
            console.log("Response successfully sent");
            //return false;
        }

    });
};

setInterval(checkupdate, 2000);

The problem was with the server side. The response object was not available (it was undefined) when server wanted to reply and therefore the response was not being sent. I may have overlooked the error in the node.js's console.

This is almost a complete example of long polling with node.js with MYSQL database. This script will wait for fresh data to become available before replying to the ajax request. If fresh data (in MYSQL) is not available within 30 seconds of the request then a fake reply is made so that the request does not time out. There is a condition in ajax's success callback that re-initiates this ajax request when this demo response is received, therefore making this an infinite loop.

I have successfully tested code above and it seems to work fine. I ran the script and then updated the data in my database (mainly the time_added field) and this triggered a reply to my waiting ajax call with new data from node.js's server.

I hope this code helps someone out there.

Checkout tutorial here for further explanation: http://www.sahilsaid.com/blog/long-polling-node-js-mysql-database-ajax/

Share:
12,766
Sahil
Author by

Sahil

Please check out my portfolio website at: http://www.sahilsaid.com

Updated on June 08, 2022

Comments

  • Sahil
    Sahil about 2 years

    I have following server code.

    var http = require('http');
    var mysql = require('mysql');
    var querystring = require('request');
    var util = require('util');
    var url = require('url');
    
    var singer_name;
    var currentmodif, lastmodif;
    var requests=[];
    var response;
    
    var connection = mysql.createConnection({
      host     : 'localhost',
      user     : 'someone',
      password : 'xxxxxxx',
      database : 'rest',  //mysql database to work with (optional)
    });
    connection.connect(); //connect to mysql
    
    connection.query('SELECT * FROM musics WHERE id=1', function(err, rows, fields) {
      if (err) throw err;
    
      singer_name=rows[0].singer_name;
      currentmodif=rows[0].time_added;
    });
    
    
    http.createServer(function (req, res) {
        console.log('request received');
    
        requests.push({
            response: res,
            timestamp: new Date().getTime()
        });
    
        if(req.method=='GET'){
            var url_parts = url.parse(req.url,true);
            lastmodif = url_parts.query.timestamp;
        }
    
        //check_update(req, res);
    
    }).listen(9000);
    
    
    setInterval(function() {
    
        var expiration = new Date().getTime() - 30000;
    
        for (var i = requests.length - 1; i >= 0; i--) {
            //console.log("Request timestamp: "+requests[i].timestamp+" Expiration : "+expiration);
            response = requests[i].response;
            if (requests[i].timestamp < expiration) {
                console.log("The condition is met");
                response.writeHead(200, {
                'Content-Type'   : 'text/plain',
                'Access-Control-Allow-Origin' : '*'
                });
    
                // return response
                response.write('_testcb(\'ok\')', 'utf8');
                response.end();
                //break;
            }
        }
    
        connection.query('SELECT * FROM musics WHERE id=1', function(err, rows, fields) {
            if (err) throw err;
            currentmodif=rows[0].time_added;        
                //console.log("currentmodif: "+currentmodif+" lastmodif: "+lastmodif);
            if (currentmodif > lastmodif){
                singer_name=rows[0].singer_name; 
                var _arrays = {'singer_name': singer_name, 'time': currentmodif}
                var data = "_testcb"+"("+JSON.stringify(_arrays)+")";
                response.writeHead(200, {
                'Content-Type'   : 'text/plain',
                'Access-Control-Allow-Origin' : '*'
                });
                if (response.end(data))
                console.log("Response successfully sent");
                //return false;
            }
    
        });
    }, 2000);
    

    and Client code:

    <html>
    <head>
        <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
        <title>Node.js Ajax test</title>
    </head>
    <body>
    
    </body>
    <script>
    var timestamp = "1380020402";
    function callNode() {
    
    var time = "1380020402";
        $.ajax({
            url: 'http://xx.xxx.xx.xxx:9000/',
            dataType: "jsonp",
            data: {"timestamp":timestamp},
            type: 'POST',
            jsonpCallback: "_testcb",
            cache: false,
            timeout: 35000,
            success: function(response, code, xhr) {
                if ('ok' == response) {
                    callNode();
                    return false;
                }
    
                console.log(response);
    
                timestamp = response.time;
                // make new call
                callNode();
            },
            error: function(jqXHR, textStatus, errorThrown) {
                console.log('error ' + textStatus + " " + errorThrown);
            }
        });
    }
    $(function () {
        callNode();
    });
    </script>
    </html>
    

    I am trying to do a long polling. So until a data in database is updated, the response to ajax request should be paused but the above code is not working. I am making the ajax request from different domain and therefore using jsonp.

    Exact problem is that currently when the data is changed in database the response doesn't get sent. It works every now and then but it is not consistently reliable.

    Another problem is that the code block for time out is not working. If the request is 30 seconds old then a blank response should be sent in order to avoid the timeout from ajax.

    If someone can help then I would appreciate.

    Cheers.