我有一个针对iOS和Android设备的jQuery Mobile网络应用。应用程序的一个组件是后台任务,它定期检查a。)对本地数据的更改以及b。)与服务器的连接。如果两者都为真,则任务会推动更改。
我正在使用一个简单的基于setTimeout()的函数来执行此任务。每个失败或成功条件都会在后台任务上调用setTimeout(),确保它以30秒的间隔运行。我使用上一个任务运行时的时间戳更新状态div以进行调试。
在任何桌面浏览器中,这都可以正常工作;但是,在iOS或Android上,经过一段时间后,任务就会停止执行。我想知道这是否与设备的省电设置有关 - 当iOS进入待机状态时,是否会终止JavaScript执行?这似乎发生了。
如果是这样,恢复的最佳方式是什么?是否有可以挂钩的唤醒事件?如果没有,那么还有哪些其他选项不涉及依赖于用户交互的事件(我不想将整个页面绑定到单击事件只是为了重新启动后台任务)。
答案 0 :(得分:29)
当浏览器页面没有聚焦时,看起来在MobileSafari上暂停了Javascript执行。似乎setInterval()事件迟到了,只要浏览器聚焦就会触发它们。这意味着我们应该能够保持setInterval()运行,并假设如果setInterval函数花费的时间比平时长得多,则浏览器会丢失/重新获得焦点。
此代码在从浏览器选项卡切换回来后,从另一个应用程序切换回来后,以及从睡眠状态恢复后发出警报。如果您将阈值设置为比setTimeout()稍长,则可以假设如果触发此超时将无法完成。
如果你想保持安全:你可以保存你的超时ID(由setTimeout返回)并将其设置为比你的超时更短的阈值,然后再次运行clearTimeout()和setTimeout()。 / p>
<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>
编辑:看起来这有时会在恢复时连续触发一次,所以你需要以某种方式处理它。 (让我的安卓浏览器彻夜难眠后,它醒来了两个alert()s。我打赌Javascript在完全睡眠之前的任意时间恢复了。)
我在Android 2.2和最新的iOS上测试过 - 一旦你从睡眠状态恢复,它们都会发出警报。
答案 1 :(得分:4)
当用户切换到另一个应用程序或屏幕休眠时,计时器似乎暂停,直到用户切换回应用程序(或屏幕唤醒)。
Phonegap有一个resume事件你可以听,而不是轮询状态(以及pause事件,如果你想在失焦前做事情)。在deviceReady触发后开始收听它。
document.addEventListener("deviceready", function () {
// do something when the app awakens
document.addEventListener('resume', function () {
// re-create a timer.
// ...
}, false);
}, false);
我使用带有phonegap的角度,我实现了一个服务,为我管理一定的超时但基本上你可以创建一个设置定时器的对象,取消定时器,最重要的是,更新定时器(更新是在&#39;简历&#39;事件)。
在角度我有一个可以附加数据的范围和根范围,我的超时是全局的,所以我将它附加到根范围但是为了这个例子的目的,我只是将它附加到文档对象。我不会宽恕,因为你需要将它应用于某种范围或命名空间。
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;
}
}
};
};
您可以让管理器函数采用毫秒参数而不是将其传递给集合,但在我编写的角度服务之后,这又是有点建模的。操作应该清晰简洁,以便与他们合作并将其添加到您自己的应用程序中。
var timeoutCallback = function () { console.log('timer fired!'); };
var manager = timeoutManager();
manager.setTimer(20000);
一旦在事件监听器中收到resume事件,您将需要更新计时器,如下所示:
// do something when the app awakens
document.addEventListener('resume', function () {
var manager = timeoutManager();
manager.updateTimer();
}, false);
超时管理器还有cancelTimer()
,可用于随时终止计时器。
答案 2 :(得分:2)
您可以根据@jlafay答案使用此课程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()
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();
希望它有所帮助,谢谢...
答案 3 :(得分:0)
您应该使用Page Visibility API (MDN),几乎所有地方都支持。它可以检测页面或标签是否再次变得可见,然后您可以继续超时或执行某些操作。