当 iPhone/Android 进入睡眠状态时,JavaScript 执行(settimeout 等)会发生啥?

Posted

技术标签:

【中文标题】当 iPhone/Android 进入睡眠状态时,JavaScript 执行(settimeout 等)会发生啥?【英文标题】:What happens to JavaScript execution (settimeout, etc.) when iPhone/Android goes to sleep?当 iPhone/Android 进入睡眠状态时,JavaScript 执行(settimeout 等)会发生什么? 【发布时间】:2011-09-26 11:09:25 【问题描述】:

我有一个针对 iosandroid 设备的 jQuery Mobile Web 应用程序。应用程序的一个组件是一个后台任务,它定期检查 a.) 本地数据的更改和 b.) 与服务器的连接。如果两者都为真,则任务推送更改。

我正在使用一个简单的基于 setTimeout() 的函数来执行此任务。每个失败或成功条件都会在后台任务上调用 setTimeout(),确保它以 30 秒的间隔运行。出于调试目的,我使用最后一个任务运行时的时间戳更新了一个状态 div。

在任何桌面浏览器中,这都可以正常工作;但是,在 iOS 或 Android 上,经过一段时间后,任务会停止执行。我想知道这是否与设备的节能设置有关-当iOS进入待机状态时,它会终止javascript执行吗?这似乎就是发生的事情。

如果是这样,最好的恢复方式是什么?是否有我可以加入的唤醒事件?如果没有,还有哪些其他选项不涉及挂钩依赖于用户交互的事件(我不想将整个页面绑定到单击事件以重新启动后台任务)。

【问题讨论】:

注意,苹果在 iOS8 中破坏了大部分问题,并且还没有修复它 - openradar.me/radar?id=5895945212395520 【参考方案1】:

当浏览器页面未获得焦点时,MobileSafari 上的 Javascript 执行似乎已暂停。似乎如果 setInterval() 事件迟到了,只要浏览器聚焦,它们就会被触发。这意味着我们应该能够保持 setInterval() 运行,并假设如果 setInterval 函数花费的时间比平时长得多,浏览器会失去/重新获得焦点。

此代码会在从浏览器选项卡切换回来、从另一个应用切换回来以及从睡眠状态恢复后发出警报。如果你设置的阈值比你的 setTimeout() 长一点,你可以假设如果这个触发你的超时不会结束。

如果您想保持安全:您可以保存您的超时 ID(由 setTimeout 返回)并将其设置为比您的超时更短的阈值,然后在触发时再次运行 clearTimeout() 和 setTimeout()。

<script type="text/javascript">
var lastCheck = 0;

function sleepCheck() 
        var now = new Date().getTime();
        var diff = now - lastCheck;
        if (diff > 3000) 
                alert('took ' + diff + 'ms');
        
        lastCheck = now;


window.onload = function() 
        lastCheck = new Date().getTime();
        setInterval(sleepCheck, 1000);

</script>

编辑:看起来这有时会在简历中连续触发多次,因此您需要以某种方式处理它。 (让我的 android 浏览器睡一整夜后,它会唤醒两个 alert()。我敢打赌 Javascript 会在完全睡眠之前的某个任意时间恢复。)

我在 Android 2.2 和最新的 iOS 上进行了测试 - 一旦你从睡眠中恢复,它们都会发出警报。

【讨论】:

经过测试并且可以工作,但它会在某个时间点停止工作。就像一夜之间。安卓 5.1.1,铬。【参考方案2】:

当用户切换到另一个应用或屏幕休眠时,计时器似乎会暂停,直到用户切换回应用(或屏幕唤醒时)。

Phonegap 有一个resume 事件,你可以收听而不是轮询状态(如果你想在它失去焦点之前做某事,还有一个pause 事件)。您在 deviceReady 触发后开始收听它。

document.addEventListener("deviceready", function () 
    // do something when the app awakens
    document.addEventListener('resume', function () 
        // re-create a timer.
        // ...
    , false);
, false);

我将 Angular 与 phonegap 一起使用,并且我实现了一个服务,它为我管理一定的超时,但基本上你可以创建一个对象来设置计时器,取消计时器,最重要的是,更新计时器(更新是在'resume' 事件)。

在 Angular 中,我有一个范围和根范围,我可以将数据附加到,我的超时是全局的,所以我将它附加到根范围,但出于本示例的目的,我将简单地将它附加到文档对象。我不宽恕这种做法,因为您需要将其应用于某种范围或命名空间。

var timeoutManager = function () 
    return 
        setTimer: function (expiresMsecs) 
            document.timerData = 
                timerId: setTimeout(function () 
                    timeoutCallback();
                ,
                expiresMsecs),

                totalDurationMsecs: expiresMsecs,
                expirationDate: new Date(Date.now() += expiresMsecs)
            ;
        ,

        updateTimer: function () 
            if (document.timerData) 
                //
                //  Calculate the msecs remaining so it can be used to set a new timer.
                //
                var timerMsecs = document.timerData.expirationDate - new Date();

                //
                //  Kill the previous timer because a new one needs to be set or the callback
                //  needs to be fired.
                //
                this.cancelTimer();

                if (timerMsecs > 0) 
                    this.setTimer(timerMsecs);
                 else 
                    timeoutCallback();
                
            
        ,

        cancelTimer: function () 
            if (document.timerData && document.timerData.timerId) 
                clearTimeout(document.timerData.timerId);
                document.timerData = null;
            
        
    ;
;

您可以让管理器函数接受一个毫秒参数,而不是将其传递给 set,但这又在某种程度上模仿了我编写的 Angular 服务。操作应该足够清晰和简洁,以便对它们进行一些操作并将它们添加到您自己的应用中。

var timeoutCallback = function ()  console.log('timer fired!'); ;
var manager = timeoutManager();
manager.setTimer(20000);

一旦在事件侦听器中获得恢复事件,您将需要更新计时器,如下所示:

// do something when the app awakens
document.addEventListener('resume', function () 
    var manager = timeoutManager();
    manager.updateTimer();
, false);

超时管理器还有cancelTimer(),可以用来随时杀死定时器。

【讨论】:

如果它是开源的,您介意发布一个指向您的 Angular 服务的链接吗? @gabereal 对不起,我没有链接。它不是开源的。 好的。感谢您的答复。只是好奇。浏览器运行时是否只是暂停并从中断处继续? 恢复事件在 Safari 上不起作用 (developer.mozilla.org/en-US/docs/Web/Events/resume)【参考方案3】:

你可以根据@jlafay answer使用这个类github.com/mustafah/background-timer,你可以使用如下:

咖啡脚本

timer = new BackgroundTimer 10 * 1000, ->
    # This callback will be called after 10 seconds
    console.log 'finished'

timer.enableTicking 1000, (remaining) ->
    # This callback will get called every second (1000 millisecond) till the timer ends
    console.log remaining

timer.start()

javascript

timer = new BackgroundTimer(10 * 1000, function() 
    // This callback will be called after 10 seconds
    console.log("finished");
);

timer.enableTicking(1000, function(remaining) 
    // This callback will get called every second (1000 millisecond) till the timer ends
    console.log(remaining);
);

timer.start();

希望对你有帮助,谢谢...

【讨论】:

它有效,谢谢。但它依赖于未提及的时刻.js 库。 这会监听github.com/mustafah/background-timer/blob/master/… Line 20 上的“resume”事件。但是“resume”事件在 Safari 上不起作用,因此它在 iOS 上不起作用。 developer.mozilla.org/en-US/docs/Web/Events/resume【参考方案4】:

您应该使用几乎所有地方都支持的Page Visibility API (MDN)。它可以检测页面或选项卡是否再次可见,然后您可以恢复超时或执行一些操作。

【讨论】:

如果手机在页面上从锁定状态变为解锁状态,则不会触发页面可见性 API。仅当您从选项卡返回到应用程序选项卡或从最小化到最大化时,它才有效。我刚刚在 iOS Safari (iOS 13.7) 上进行了测试。

以上是关于当 iPhone/Android 进入睡眠状态时,JavaScript 执行(settimeout 等)会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章

当手机进入睡眠状态时,颤振热重载停止工作

当计算机进入“睡眠”状态时做某事

当我的 Mac 进入睡眠状态时,我的应用程序会发生啥?

当手机从睡眠中唤醒时加载应用程序/活动

让 iPhone 进入睡眠状态会隐藏 tableview 单元格 [重复]

java thread.sleep 也让 swing ui 进入睡眠状态