1

Originally before I understood long-polling, I had this code:

     var updateMessages = function() {
        var conv_id = [];
        var lastMessages = [];

        $('.user').each(function(){
            conv_id.push($(this).find('p').attr('id'));
        });
        if($('.rightP').find('.msg .msg_block').length!=0){
            $('.rightP').find('.msg .msg_block').each(function(){
                if(($('.rightP').find('.msg .msg_block p').length)==0){
                }else {
                    lastMessages.push($(this).find('p:last-child')[0].dataset.created_at);
                }
            });
        }
        $.ajax({
            type: "POST",
            url: 'create/populate',
            data: {
                'from': lastMessages,
                'conv_id':conv_id
            },
            success: function(messages) {
                console.log(messages);
                $.each(messages, function() {
                    appendMessage(this);
                });
            },
            error: function(xhr,textStatus,errorThrown) {
                console.log(xhr.responseText);
            },
            complete: function() {
                window.setTimeout(updateMessages, 2000);
            },
            dataType: 'json'
        });
    };
updateMessages();

However, one person commented that this code isn't long-polling. So I researched and adjusted some codes above like so :

 ...
     complete: function() {
                updateMessages(); //also tried updateMessages;
            },
     timeout:30000,
     dataType: 'json'
...

but it ran into problems such as not polling at all and the messages won't update. How can I adjust my original code to do long-polling? Improvement of code is a bonus. Thank you!

gente note : I don't use web sockets bcoz of legacy browser compatibility issues. I also don't use nodejs because my shared-server does not allow long-running processes.


PHP code (in my controller)

public function index()
    {
        $timestamps = Input::get('from'); //timestamp of latest message
        $conv_id = Input::get('conv_id');
        $allMessages = Messages::whereIn('conv_id',$conv_id);
        if(is_null($timestamps)){
           $messages = $allMessages->orderBy('created_at','desc')->take(10);
        }else{
           asort($timestamps);
           $messages = $allMessages->where('created_at','>',end($timestamps));
        }
        return $messages->get()->reverse();
    }
John Evans Solachuk
  • 1,953
  • 5
  • 31
  • 67
  • [**`setInterval`**](https://developer.mozilla.org/en/docs/Web/API/window.setInterval) But you should look into [**Web Sockets**](http://www.html5rocks.com/en/tutorials/websockets/basics/) – Darren Jun 20 '14 at 03:38
  • @Darren You mean I should just change setTimeout to setInterval? Btw, I don't use web sockets bcoz of legacy browser compatibility issues. I also don't use nodejs because my shared-server does not allow long-running processes. – John Evans Solachuk Jun 20 '14 at 03:40
  • This quick [**JSFiddle**](http://jsfiddle.net/WyS2W/2/) demo illustrates setInterval (check console). Or you should look at [**This SO Answer**](http://stackoverflow.com/a/1086448/2518525) – Darren Jun 20 '14 at 03:47
  • You'll need to make your server code keep waiting for data for say 25 seconds too. Setting the timeout on javascript alone would not do it. Your server will probably still respond within a second, not thirty. – Unnawut Jun 20 '14 at 03:50
  • @Unnawut You mean I should encapsulate all my server code in a while loop in my server and use sleep() for say 25 seconds too? Code updated above for reference. – John Evans Solachuk Jun 20 '14 at 04:09
  • 1
    @Mark Yes! Your javascript looks reasonable to me. But I still don't see the loop and sleep part in php, though. Note that I also have no idea if there is a performance issue doing this with PHP with concurrent connections. – Unnawut Jun 20 '14 at 04:12
  • @Unnawut alright thanks man! I didn't want to edit my original code because I didn't want to make it too long or disturb it. Maybe you can modify my code with while looop and sleep and put it as an answer. Yeah, concurrent connections are a problem as I've read in other sites. I'll post another question if I run into another problem. – John Evans Solachuk Jun 20 '14 at 04:20

1 Answers1

2

Note that the code below only tries to demonstrate how jQuery + PHP long-polling could work. And may have issues such as performance with concurrency, etc. I'm trying to demonstrate a simple long-polling script based on the question.

To be able to scale and support long-polling properly, I would suggest using technologies like Web Sockets that Darren mentioned, Socket.io, and possibly ReactPHP for alternative to node.js.

Client-side javascipt:

var updateMessages = function() {
    var conv_id = [];
    var lastMessages = [];

    // your code to get conv_id and lastMessages here

    $.ajax({
        type: "POST",
        url: 'create/populate',
        timeout: 30000,
        dataType: 'json',
        data: {
            'from': lastMessages,
            'conv_id': conv_id
        },
        success: function(messages) {
            // your success code here
        },
        error: function(xhr,textStatus,errorThrown) {
            console.log(xhr.responseText);
        },
        complete: function() {
            window.setTimeout(updateMessages, 2000);
        },
    });
};

updateMessages();

Your PHP controller:

public function populate()
{
    $timeout_in_seconds = 20;
    $interval_in_seconds = 5;

    $start_time = time();
    $timeout = false;

    $timestamps = Input::get('from'); //timestamp of latest message
    $conv_id = Input::get('conv_id');

    // While we don't have any new messages, and haven't reached timeout yet.
    while (empty($messages) && !$timeout) {

        $allMessages = Messages::whereIn('conv_id', $conv_id)
            ->orderBy('created_at','desc');

        if (empty($timestamps)) {
           $messages = $allMessages->take(10)->get()->reverse();
        } else {
           asort($timestamps);
           $messages = $allMessages->where('created_at', '>', end($timestamps))->get()->reverse();
        }

        $timeout = (time() - $start_time) > $timeout_in_seconds;

        // If there is message or timeout, break out of the while loop and return the messages immediately.
        if (!empty($messages) || $timeout) {
            break;
        }

        sleep($interval_in_seconds);
    }

    return $messages;
}
Unnawut
  • 7,500
  • 1
  • 26
  • 33
  • Thanks! I think I may have found a [solution](http://stackoverflow.com/questions/16080692/long-polling-with-ajax-and-php-apache-freezes?rq=1). What do you think? Can this be done with shared-servers? – John Evans Solachuk Jun 20 '14 at 05:56
  • To be honest I don't really think you'll be beneficial from doing this in shared hosting. If you can accept a 1 or more minute delay, doing an interval call every 1 minute or more from javascript would actually be much more efficient than 30-sec fake long polling I put above. – Unnawut Jun 20 '14 at 06:18
  • yeah, I thought so. Shared hosting is really a pain. I guess I'll just stick with my normal polling. Thanks anyway! – John Evans Solachuk Jun 20 '14 at 06:21
  • On a side note, cloud servers are quite cheap these days, some start at $5 per month, maybe consider moving away from shared hosting in the future? – Unnawut Jun 20 '14 at 06:24