长轮询的 jQuery.ajax() 在手机休眠后无法回调?

18
我的网络应用程序使用“长轮询”方法来及时获得服务器最新数据。服务器只在有新数据时才作出响应,这可能间隔数分钟。(这是一个供暖控制系统,您只能在室温变化或有人更改设置时才会看到更新)。

我的Web应用程序使用“长轮询”方法来及时获得服务器最新数据。服务器只在有新数据时才作出响应,这可能间隔数分钟。(这是一个供暖控制系统,您只能在室温变化或有人更改设置时才会看到更新)

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

它在桌面浏览器和手机上工作良好,除了一个情况。我发现在使用Chrome的安卓手机上,当手机休眠超过大约10分钟后会发生一些奇怪的事情。POST请求似乎被丢弃了,这很合理,因为手机处于睡眠状态。在Chrome调试器的网络标签中,POST请求的状态文本显示为(canceled)。

问题在于,当我在请求被取消时唤醒手机时,success()或error()函数都不会被调用,我的Web应用程序永远不会得到更新。$.ajax()已经破坏了它的承诺,没有回调给我。

该问题只发生在某些设备上。通过向朋友借用设备,我已经能够进行一些临时测试。到目前为止,我只看到在安卓手机上存在此问题,但是如果手机连接到充电器,则不会出现此问题。我没有在任何平板电脑、苹果设备或Windows PC上看到过这个问题。

我已经尝试将超时添加到ajax设置中:

     timeout: 120 * 1000,

这有帮助,因为在设备唤醒后,直到2分钟后才最终调用error()函数。但我希望用户能在1或2秒内看到更新。我不想将超时时间设置得太短,因为这会创建不必要的服务器流量。

我还尝试通过查找一秒钟的setInterval中的延迟来检测设备是否处于休眠状态,如Can any desktop browsers detect when the computer resumes from sleep? 所述。当我检测到唤醒时,我会中止post并开始另一个。这在大多数情况下都有所帮助。但事实证明它不可靠。有时候,在睡眠期间,时间事件似乎正常运行,而post请求仍然被取消。而且它也不感觉像是一个可靠的解决方案。

我正在使用jQuery的最新版本(2.1.2)和Chrome(47)。


尝试将当前的 Promise 全局存储,然后当进入此失败状态时,检查 Promise 的 .state()。它包含什么?如果它包含除了 "pending" 以外的任何内容,您可以将其与 @spozun 的解决方案结合使用,在休眠期后重新启动事物,而不必取消请求。 - Kevin B
5个回答

0

那么像这样的更高级别的观察者函数怎么样:

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();
});

你知道当手机唤醒时,$(window).focus会触发,所以你尝试像Almis建议的那样使用updater(),但加上一个安全定时器。如果updater被触发(在笔记本电脑或iOS上),则定时器被取消,一切都很好,但如果updater死了,则安全定时器在6秒后触发,并通过调用initializeAll()重新启动整个应用程序。

0

我不确定这个会不会有效,现在无法测试,但可以试一下。

$(window).focus(function() {
    updater();
});

在浏览器按预期工作并保持请求打开的情况下,这是否会发出另一个请求? - Katana314
@Katana314 只有在用户在其他标签页上(我想在移动设备上也适用于非活动状态)并且聚焦我的标签页时,才会触发此操作。当然,您还可以使用某种标志来查看更新程序是否按预期工作。 - Almis
很遗憾,这并不起作用。我尝试过了。$(window).focus()在手机从睡眠中唤醒时不会调用该函数。它似乎只在您在Chrome浏览器中切换选项卡时才调用该函数。 - James
@Almis 谢谢,我非常感激你的努力。我已经尝试了很多天,这是一个新的想法。 - James
@James,我无法重现你的问题。我在我的服务器上设置了一个演示应用程序,在响应之前只延迟了5秒钟,然后我最小化了浏览器,让手机进入睡眠状态,回到浏览器后它仍然可以正常工作... 在Chrome 47上测试过。 - Almis

0
过去我曾经遇到过当手机进入睡眠状态时JavaScript调用被挂起的问题。最终我采用了window.setInterval(),它似乎会被挂起,但在手机唤醒后又会恢复运行。
因此,我建议设置一个间隔,每隔一段时间取消调用并重新初始化。这可能有助于它在手机睡眠期间保持运行。
大致如下:
var myCall = $.ajax({...});

Window.setInterval (refreshCall(), 10000);

function refreshCall (){
    myCall.abort ();
    myCall = $.ajax({...});
}

0
最好的答案是“不要那样做”。当服务器在服务器端跟踪更改时,您正在等待服务器响应。只需让服务器响应,然后让jQuery定期ping。如果您想要防止实际刷新而没有状态更改,则可以包括lastchanged或haschanged,但如果服务器无论哪种方式都在执行相同的工作,请让其响应并等待下一个轮询。
setInterval(function () { 
    $.post({"/listen", version, function (data) {
        if(data.haschanged)
            version = handleUpdates(data);
    }).fail(function () {
        // any error correction on failed call
    });
}, 1000);

0
如何使用setInterval,记录它被调用的时间,然后将上次调用时间与当前时间进行比较 - 如果您的间隔是10秒,并且自上次运行以来经过的时间为5分钟,则可以假定您刚从睡眠中醒来?然后中止当前的ajax调用并重新启动它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接