手机息屏导致 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就万事大吉了么?

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

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

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

好了,代码就是下面这一小点

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();   // 激活状态重新获取数据
      }
   }
}

复制代码

到此为止,已经解决了app切到后台运行倒计时不准的bug了!!真是值得庆幸的事情。 只是后面项目上线之后我们的测试小姐姐还发现了一个bug,就是iOS系统上面,长按屏幕仍然会导致倒计时暂停的问题。。我的天!!!但由于项目马上就要投入使用了,所以我们组长大手一挥说不用改了,下次再做优化。

我后来在网上找了一下,原因是IOS机制的问题,将未激活的页面线程直接阻塞,怎么解决这个问题呢,发现有人说可以用js worker来解决,思路是js worker可以构造出另一个线程出来,与我们的主线程并行,单独做倒计时这一块,但是相关文章很少,相关的demo更少,我只看到了一个基于jquery写的单个倒计时的demo,然而我的页面基于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秒。

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

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

转载于:https://juejin.im/post/5bfe6f11e51d453c6c05f61d

你可能感兴趣的:(手机息屏导致 js 定时器时间不准问题及解决方法)