h5页面切换到后台再返回 js 定时器时间不准确解决方法

这两天做迭代任务遇到了一个难题,现在找到了解决方法,为了避免忘记,在这里记录下来。

项目描述:一个基于vue写的h5页面,根据后端返回的当前服务器时间做一个倒计时(我是用setInterval做的)。该h5页面分别嵌套到微信公众号,和原生app中。

 

问题描述:使用home键把页面切到后台运行,倒计时就暂停了,再切回到前台运行回去看倒计时,时间不准确(具体表现为:切出之前是20:20:20,切出等待10秒再切回来,时间仍然停留在20:20:20,并没有减去10秒)。

 

我百度了一下,看到了一个大神的回答:

【PC上的Firefox,Chrome和Safari等浏览器,都会自动把未激活页面中的JavaScript定时器(setTimeout,setInterval)间隔最小值改为1秒以上。这是因为间隔很小的定时器一般用来做UI更新(例如用定时器实现的动画),让用户不可见的页面上的定时器跑慢一些,既节省资源又不会影响体验。对移动浏览器来说,内存,CPU,带宽等资源更加宝贵,设备移动的上浏览器往往会直接冻结所有未激活页面上的所有定时器。】

 

怎么办呢?

我立马想到的是在定时器实时获取当前服务器时间,但这样肯定不行,服务器时间是通过接口获取的,这样就会大量造成服务器的压力。

后来想到可以监听浏览器前后台的切换状态,这样我们就可以可用这个状态取去做一些事情了,比如切换回来的时候重新请求一下接口获取服务器时间再做倒计时,这样既能大大减少了服务器的压力,又能得到准确的服务器时间,从而让倒计时变得准确无误。

代码如下:

mounted() {
   let _this = this;
   document.addEventListener("visibilitychange", _this.checkViChange); 
},

methods: {
    checkViChange() {
       if (!document.hidden) {
          this.getData();   //重新调用getData 方法去请求数据
       }
    }
}

这样就能很好的捕抓浏览器的行为去拿数据了。

至少在PC端调试的时候是没有任何问题的。

 

以为PC端调试好就万事大吉了么?

心想着完美解决问题了的我,兴高采烈的把代码把代码推送上去,然后在公众号看是OK的(微信有自己内置的浏览器,我自己认为它相当于PC端),再到应用端看,竟然无动于衷!不管你怎么切出切入,硬是没有监听的到!真是令人崩溃,看来还要针对原生应用程序做单独处理。

我又去百度了一波,app从活动状态转入后台,绝大部分app通常在几秒内就从后台变成了挂起。我想就是这个原因,导致我们的js被阻塞了,停止运行了,所以我们写好的监听函数才监听不到页面的状态。

这样一来前端自己解决是不可能的了,只能让应用程序开发的小哥哥帮忙解决了,思路很简单,应用程序自己可以监听的页面的状态,还能调用前端H5写给它执行的方法,相当在把前之前端自己调用的方法让app帮我们做。原生app与vue的交互可以参考这篇文章:https://blog.youkuaiyun.com/lvlemo/article/details/78905273 。

好了,那我们的代码就是下面这一小点,

mounted() {
    let _this = this;
    document.addEventListener("visibilitychange", _this.checkViChange);   //这是之前写的监听函数不用删除
    window.checkVisible = _this.checkVisible;  // checkVisible是写给app调用的
  },
methods: {
    checkVisible: function(flag) {  // flag 是app 返回给我们的标识,告诉我们app现在处于什么状态,如 true是激活,false是挂起
      var _this = this;
      if(flag) {
        _this.getData();   // 激活状态重新获取数据
      }
   }
}

到此为止,我们已经解决了应用切到后台运行倒计时不准的错误了!真是值得庆幸的事情。

 

只是后面项目上线之后我们的测试小姐姐还发现了一个错误,就是iOS的系统上面,长按屏幕仍然会导致倒计时暂停的问题..我的天!但由于项目马上就要投入使用了,所以我们组长大手一挥说不用改了,下次再做优化。

我后来在网上找了一下,原因是IOS机制的问题,将未激活的页面线程直接阻塞,怎么解决这个问题呢,发现有人说可以用js worker来解决,思路是js worker可以构造出另一个线程出来,与我们的主线程并行,单独做倒计时这一块,但是相关文章很少,相关的演示更少,我只看到了一个基于jQuery的写的单个倒计时的演示,然而我的页面基于VUE开发,有多个倒计时,我尝试过改造,但是由于文档太少,参考价值不多,加上本人体力,智力不支,暂时放弃了继续研究,有兴趣的朋友可以研究一下,如果研究出来了希望能教教我,哈哈哈!先谢谢!

除了js worker的方法之外,其实我觉得同样可以让app帮忙处理的嘛!道理是同一个的,相信这不是什么大问题。

这篇文章写到这里差不多了。基本记录了我在这个小迭代中遇到的一些坑及解决办法,以后就不怕忘记了。

 

-----------------------------------------------------------------------------------------------------------------------------------------------

补充1:

之前说到IOS长按屏幕倒计时会暂停,长按之后倒计时恢复计时,但是会有暂停时间的时间差,也就是说你长按了10秒,那么就有10秒的时间差。之前有提到用 js worker的方法之外,同样可以让app帮忙处理。后来在另一个版本优化的时候我用了另一种方法解决。

说明:首先能用这种方法解决的前提是:倒计时是利用 【截止时间】-【服务器当前时间】,得到的差值再转换成具体时间格式。其中【服务器当前时间】是以每秒递增的。(我想很多人是把差值做每秒递减来做的倒计时,这种做法不适合我说的这种方法,如果是这么做的话请参考补充2)。

那么如果使用的和我同一种做法的可以参考我的方法解决问题。

思路:请求接口的时候获取到服务器的当前时间,然后再获取本机系统的当前时间,然后做一个差值为gapTime存起来,比如 gapTime = 2000,用于后面做比较。

然后每秒倒计时的时候加上同样的操作,即 获取本机时间 和 【服务器当前时间】做差值记为gapTime_1。

然后再比较 gapTime_1 和 gapTime,如果相等不做处理,如果不相等,则再把这两个差值做一次差值记为 gap_val,然后把gap_val累加到上文提到的用来做递增的【服务器当前时间】上。其实意思很简单,没有长按之前【服务器当前时间】是每秒递增的,gapTime 和 gapTime_1也总是保持着2000的差值,然后长按10秒,注意,这10秒钟里【服务器当前时间】是不会递增的,等到我长按了10秒回来,再重新走倒计时,得到gapTime_1就将会是12000,那么gap_val就是 12000 - 2000 = 10000。然后把这10000累加到【服务器当前时间】就行了。

 

-----------------------------------------------------------------------------------------------------------------------------------------------

补充2:

也有很多人是用【截止时间】-【服务器当前时间】的差值记为 decTime(剩余时间)递减做的倒计时。

其实和上面的相似,只是上面的是 【服务器当前时间】+ gap_val。

而这种情况下是,仍然先每秒倒计时的时候把【服务器当前时间】递增1秒,然后按照补充1的方法算出 gap_val,再然后decTime直接减去gap_val就可以了,简而言之就是,如果长按10秒,那么decTime就一次性减去10秒。

-----------------------------------------------------------------------------------------------------------------------------------------------

或许有人会说,本机系统时间是可以随便调的,就会导致差值不准确。其实这个问题是不存在的,因为你要调本机时间的时候你已经切到后台运行了,再回来就是走前面提到的切换前后台的逻辑了。

到这里这个倒计时的问题就完全解决了。由于我的代码涉及到多个倒计时并且数据结构比较复杂,所以就不贴出这部分代码了。免得误导。
 

 

 

<think>嗯,用户让我介绍一下H5定时器。首先,我需要确定H5指的是HTML5,对吧?对的,因为H5通常是HTML5的简称。那HTML5中的定时器应该是指JavaScript中的setTimeout和setInterval这两个函数,可能还有requestAnimationFrame。用户可能更关注传统的定时器方法。 首先,我应该回顾一下这两个方法的基本用法。setTimeout用于在指定的延迟后执行一次函数,而setInterval则是每隔一定时间重复执行函数。这些都是前端开发中常用的功能,比如延迟加载、轮询或者动画效果。 接下来,要确保自己记得正确的语法。比如setTimeout的函数签名是setTimeout(callback, delay, ...args),其中callback是要执行的函数,delay是毫秒数,args是传递给callback的参数。同样,setInterval的语法类似,只是会重复执行。 然后,可能需要提到清除定时器方法,clearTimeout和clearInterval,它们用来取消尚未执行的定时任务。这点很重要,避免内存泄漏或者必要的执行,特别是在组件卸载的时候需要清理定时器外,用户可能关心这些定时器的精度问题。由于JavaScript是单线程的,定时器的回调可能会被延迟执行,尤其是在主线程被阻塞的情况下。例如,如果一个setTimeout设置了100ms后执行,但如果主线程有其他任务在执行,可能实际执行时间会超过100ms。需要说明这一点,避免用户误解定时器的精确性。 还有,可能涉及到requestAnimationFrame,虽然它是传统的定时器,但用于动画循环,通常比setInterval更高效,因为它与浏览器的重绘同步。用户没有明确提到这个,所以可能需要简要提及,但重点还是在setTimeout和setInterval上。 然后,考虑使用场景。比如,setTimeout可以用于延迟执行某个操作,比如用户输入后的搜索建议,防抖处理;setInterval可以用于轮询服务器状态,或者定期更新页面内容。但也要提醒用户注意,长时间运行的setInterval可能导致性能问题,特别是在页面可见时,比如切换到其他标签页,这时候可以考虑使用Web Worker或者调整定时器策略。 可能还需要提到现代浏览器提供的requestIdleCallback,它允许在浏览器空闲时执行任务,但这也属于进阶内容,用户可能暂时需要,但可以作为补充知识。 安全性和最佳实践方面,需要强调清除定时器的重要性,避免内存泄漏。例如,在SPA(单页应用)中,当组件卸载时,必须清除所有由该组件创建的定时器,否则这些定时器会继续存在,可能导致错误或资源浪费。 此外,关于this的指向问题,在定时器的回调函数中,如果使用传统的函数声明,this可能指向window或global对象,而在严格模式下可能是undefined。使用箭头函数可以保留外层的作用域,这点可能需要提醒用户注意,避免出现意外的行为。 还有,可能需要给出一些代码示例,比如如何使用setTimeout和setInterval,以及如何清除它们。但根据用户的问题,可能需要具体的代码,但简要说明用法还是有必要的。 最后,总结H5定时器的应用场景、注意事项以及最佳实践,帮助用户全面理解。需要确保语言通俗易懂,结构清晰,分点说明,让用户能一步步了解H5定时器的各个方面。</think>H5(即HTML5)中的定时器功能主要通过JavaScript的 `setTimeout` 和 `setInterval` 实现,用于控制代码的执行时机。以下是分步解析: --- ### 一、核心定时器类型 1. **单次定时器:`setTimeout`** - **作用**:延迟指定时间后执行一次函数。 - **语法**: ```javascript const timerId = setTimeout(callback, delay, arg1, arg2, ...); ``` - **示例**: ```javascript setTimeout(() => { console.log('2秒后执行'); }, 2000); ``` 2. **重复定时器:`setInterval`** - **作用**:每隔指定时间重复执行函数。 - **语法**: ```javascript const timerId = setInterval(callback, delay, arg1, arg2, ...); ``` - **示例**: ```javascript let count = 0; const timer = setInterval(() => { count++; if (count >= 5) clearInterval(timer); console.log('每隔1秒执行'); }, 1000); ``` --- ### 二、清除定时器 - **必要性**:防止内存泄漏或无效执行。 - **方法**: - `clearTimeout(timerId)`:取消 `setTimeout` 定时器。 - `clearInterval(timerId)`:取消 `setInterval` 定时器。 --- ### 三、关键特性与注意事项 1. **最小延迟限制** - 浏览器通常限制最小延迟为 **4ms**(如连续嵌套的 `setTimeout`)。 2. **执行时机确定性** - 定时器回调可能在延迟后**精确执行**(受主线程任务阻塞影响)。 - 示例:若主线程繁忙,延迟100ms的任务可能实际200ms后执行。 3. **页面隐藏时的行为** - 后台标签页中的定时器可能被**节流**(如Chrome限制为1次/秒)。 --- ### 四、应用场景 1. **延迟操作**:如表单输入的防抖(Debounce)。 2. **周期性任务**:如数据轮询、动画效果。 3. **异步流程控制**:配合Promise实现超时机制。 --- ### 五、替代方案:`requestAnimationFrame` - **用途**:更高效的动画循环(与屏幕刷新率同步)。 - **优势**:避免过度渲染,浏览器自动优化。 - **示例**: ```javascript function animate() { // 动画逻辑 requestAnimationFrame(animate); } requestAnimationFrame(animate); ``` --- ### 六、最佳实践 1. **及时清理定时器**:在组件卸载或需要时调用 `clearTimeout`/`clearInterval`。 2. **避免嵌套过深**:可能导致延迟累积。 3. **慎用长周期 `setInterval`**:改用递归 `setTimeout` 更可控。 --- 通过合理使用定时器,可以实现灵活的异步逻辑控制,但需注意性能影响和执行可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值