long-poll jQuery.ajax() fails to callback after phone sleeps?
Asked Answered
S

5

18

My web app uses the 'long poll' method to keep up to date with the latest data from my server. The server only responds when it has new data, which can be many minutes apart. (It is a heating control system where you only see updates when room temperatures changes or somebody changes the settings).

var version = "0";
function updater() {
    $.ajax({
        type: "POST",
        url: "/listen",
        data: version,
        success: function (data) {
            version = handleUpdates(data);
            updater();
        },
        error: function () {
            setTimeout(updater, 1000);
        }
    });
}

It works fine on desktop browsers and on phones except in one case. I have found that on android phones with Chrome something odd happens after the phone has gone to sleep for more then about 10 minutes. The post request seems to be dropped, which I guess is reasonable since the phone is asleep. In the Chrome debugger's Network tab, the Status Text of the POST request says (canceled).

The problem is when I wake the phone up while the request is cancelled, neither the success() or error() function is called, and my web app never gets updated. $.ajax() has broken its promise to call me back.

The problem only happens on some devices. I have been able to do a few ad-hoc tests by borrowing devices off friends. So far I have only seen the problem on android phones. But not is the phone is connected to a charger. I have not seen it on any tablets, on apple devices or windows PCs.

I have tried adding a timeout to the ajax settings:

     timeout: 120 * 1000,

This helps because the error() function is eventually called up to 2 minutes after the wake up. But I'd like the user to see updates within 1 or 2 seconds. I don't want to make the timeout so short because it would create unnecessary server traffic.

I have also tried detecting whether device is asleep by looking for lateness in a one second setInterval as described in Can any desktop browsers detect when the computer resumes from sleep?. When I detect the wake up, I abort() the post and start another. This helps in most cases. But it turns out to be unreliable. Sometimes time events seem to keep ticking normally during sleep and the post request gets cancelled anyway. And it it does not feel like a reliable fix.

I am using latest version of jQuery: (2.1.2) and Chrome (47).

Santee answered 17/12, 2015 at 17:37 Comment(1)
try storing the current promise globally, then when you enter this failed state, check the .state() of the promise. What does it contain? If it contains anything other than "pending", you can use it combined with @spozun's solution to start things back up after a sleep period without unnecessarily canceling requests.Layoff
K
0

I not sure this will work or not, I cannot test it now but give it a try

$(window).focus(function() {
    updater();
});
Kellogg answered 17/12, 2015 at 18:13 Comment(5)
Wouldn't that put out another request in cases where the browser works as expected and leaves the request open?Psychochemical
@Psychochemical this will only trigger if user was on other tab (I suppose is also works for inactive on mobile) and focusing my tab. Of course you can also use some kind of flag to see if updater works as expectedKellogg
Sadly this does not work. It tried it. $(window).focus() does not call the function when the phone awakes from sleep. It only seem to call the function when you switch between tabs within chrome.Santee
@Kellogg Thanks, I really appreciate your effort. I have been trying things for days and that was a new idea.Santee
@Santee I couldn't reproduce your issue, I setup demo application on my server that just delays for 5 second before giving response, then I minimized browser, put mobile on sleep and went back to my browser and it was still working... tested on chrome 47Kellogg
N
0

I've had problems in the past with JavaScript calls getting suspended when the phone goes to sleep. The solution I ended up with was to use window.setInterval() which seems to suspend, but come back to life when the phone is woken up.

So I would recommend setting an interval which cancels the call every so often and reinitiates it. This might help it survive through a phone sleep.

Something roughly like:

var myCall = $.ajax({...});

Window.setInterval (refreshCall(), 10000);

function refreshCall (){
    myCall.abort ();
    myCall = $.ajax({...});
}
Negron answered 21/12, 2015 at 22:44 Comment(0)
D
0

How about a higher-level watcher function like this:

var restartTimer = null;
function updater(){
    $.ajax({
        type: "POST",
        url: "/listen",
        data: version,
        success: function (data) {
            version = handleUpdates(data);
            clearTimeout(restartTimer);
            updater();
        },
        error: function () {
            clearTimeout(restartTimer);
            setTimeout(updater, 1000);
        }
    });
}
//  Kick it when the phone wakes up.
$(window).focus(function(){
    restartTimer = setTimeout(function(){
        initializeAll();
    }, 6000);
    updater();
});

You know that the $(window).focus will fire when the phone wakes up, so you try updater() as Almis suggests, but with a fail-safe timer. If updater fires (on laptops or iOS), the timer is canceled and all is well, but if updater is dead, the fail-safe timer fires in 6 seconds and reboots your entire app by calling initializeAll().

Displant answered 26/10, 2016 at 14:3 Comment(0)
H
0

How about a setInterval, that stores the time it was called, then compares the last time it was called to the current time - if your interval is 10 seconds and the time passed since the last run was 5 minutes, you can assume you've just woken from sleep? Then abort the current ajax call, and restart it.

Hodge answered 7/12, 2017 at 12:50 Comment(0)
E
0

The best answer is "don't do that". You're having the server wait to respond while it tracks for changes at the server side. Just have the server respond and have the jQuery ping on an interval. You can include lastchanged or haschanged if you want to prevent actually refreshing when there's no status change, but if the server is doing the same work either way, just let it respond and wait for the next poll.

setInterval(function () { 
    $.post({"/listen", version, function (data) {
        if(data.haschanged)
            version = handleUpdates(data);
    }).fail(function () {
        // any error correction on failed call
    });
}, 1000);
Eserine answered 5/1, 2018 at 17:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.