long-poll jQuery.ajax() 在手机休眠后无法回调?

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

提问人:James 提问时间:12/18/2015 最后编辑:CommunityJames 更新时间:1/6/2018 访问量:1394

问:

我的 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 的 Android 手机上,手机进入睡眠状态超过 10 分钟后会发生一些奇怪的事情。发布请求似乎被放弃了,我想这是合理的,因为手机已经睡着了。在 Chrome 调试器的“网络”选项卡中,POST 请求的状态文本显示“(已取消)”。

问题是,当我在取消请求时唤醒手机时,既没有调用 success() 也没有调用 error() 函数,我的 Web 应用程序永远不会更新。$.ajax() 违背了给我回电话的承诺。

该问题仅在某些设备上发生。我已经能够通过从朋友那里借用设备来做一些临时测试。到目前为止,我只在安卓手机上看到了这个问题。但不是手机连接到充电器。我没有在任何平板电脑、苹果设备或 Windows PC 上看到它。

我尝试在ajax设置中添加超时:

     timeout: 120 * 1000,

这很有帮助,因为 error() 函数最终会在唤醒后 2 分钟内调用。但我希望用户在 1 或 2 秒内看到更新。我不想让超时时间这么短,因为它会产生不必要的服务器流量。

我还尝试通过在一秒钟的 setInterval 中查找延迟来检测设备是否处于睡眠状态,如任何桌面浏览器都可以检测到计算机何时从睡眠状态恢复?中所述。 当我检测到唤醒时,我中止()帖子并开始另一个帖子。这在大多数情况下是有帮助的。但事实证明这是不可靠的。有时,时间事件似乎在睡眠期间保持正常滴答作响,但无论如何,发布请求都会被取消。而且它感觉不像是一个可靠的解决方案。

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

javascript jquery ajax google-chrome 长轮询

评论

0赞 Kevin B 12/22/2015
尝试全局存储当前 Promise,然后在进入此失败状态时,检查 Promise 的 .state()。它包含什么?如果它包含“待处理”以外的任何内容,您可以将其与 @spozun 的解决方案结合使用,在睡眠期后启动备份,而不会不必要地取消请求。

答:

0赞 Almis 12/18/2015 #1

我不确定这是否有效,我现在无法测试它,但试一试

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

评论

0赞 Katana314 12/18/2015
如果浏览器按预期工作并使请求保持打开状态,这不会发出另一个请求吗?
0赞 Almis 12/18/2015
@Katana314只有当用户在其他选项卡上(我想这也适用于移动设备上的非活动状态)并专注于我的选项卡时,才会触发。当然,您也可以使用某种标志来查看更新程序是否按预期工作
0赞 James 12/18/2015
可悲的是,这是行不通的。它试过了。$(window).focus() 在手机从睡眠状态唤醒时不调用该函数。它似乎只有在您在 chrome 中的选项卡之间切换时才会调用该函数。
0赞 James 12/18/2015
@Almis谢谢,我真的很感谢你的努力。我已经尝试了好几天,这是一个新想法。
0赞 Almis 12/18/2015
@James我无法重现您的问题,我在我的服务器上设置了演示应用程序,该应用程序在给出响应之前仅延迟了 5 秒钟,然后我最小化了浏览器,将手机置于睡眠状态并返回我的浏览器,它仍在工作......在 Chrome 47 上测试
0赞 spozun 12/22/2015 #2

我过去曾遇到过 JavaScript 调用在手机进入睡眠状态时被挂起的问题。我最终得到的解决方案是使用它似乎暂停,但当手机被唤醒时又恢复了生机。window.setInterval()

因此,我建议设置一个间隔,每隔一段时间取消一次呼叫并重新启动它。这可能有助于它在手机睡眠中生存。

大致如下:

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

Window.setInterval (refreshCall(), 10000);

function refreshCall (){
    myCall.abort ();
    myCall = $.ajax({...});
}
0赞 Rob 10/26/2016 #3

像这样更高级别的观察程序函数怎么样:

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(),但使用故障安全计时器。如果更新程序触发(在笔记本电脑或 iOS 上),计时器将被取消,一切正常,但如果更新程序已死,故障安全计时器将在 6 秒内触发,并通过调用 initializeAll() 重新启动整个应用程序。

0赞 Michael Baldry 12/7/2017 #4

存储调用时间,然后将上次调用的时间与当前时间进行比较怎么样 - 如果您的间隔是 10 秒,而自上次运行以来经过的时间是 5 分钟,您可以假设您刚刚从睡眠中醒来?然后中止当前的 ajax 调用,并重新启动它。setInterval

0赞 Veritoanimus 1/6/2018 #5

最好的答案是“不要那样做”。您让服务器在跟踪服务器端的更改时等待响应。只需让服务器响应并让 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);