手機息屏致使 js 定時器時間不許問題及解決方法

這兩天作迭代任務遇到了一個難題,如今找到了解決方法,爲了不忘記,在這裏記錄下來。前端

項目描述:一個基於vue寫的h5頁面,根據後端返回的當前服務器時間作一個倒計時(我是用setInterval 作的)。該h5頁面分別嵌套到微信公衆號、和原生app中。vue

問題描述:使用home鍵把頁面切到後臺運行,倒計時就暫停了,再切回到前臺運行回去看倒計時,時間不許確(具體表現爲:切出以前是20:20:20,切出等待10秒再切回來,時間仍然停留在20:20:20,並無減去10秒)。jquery

我百度了一下,看到了一個大神的回答:後端

【 PC 上的 Firefox、Chrome 和 Safari 等瀏覽器,都會自動把未激活頁面中的 JavaScript 定時器(setTimeout、setInterval)間隔最小值改成 1 秒以上。這是由於間隔很小的定時器通常用來作 UI 更新(例如用定時器實現的動畫),讓用戶不可見的頁面上的定時器跑慢一些,既節省資源又不會影響體驗。對移動瀏覽器來講,內存、CPU、帶寬等資源更加寶貴,移動設備上的瀏覽器每每會直接凍結全部未激活頁面上的全部定時器。】瀏覽器

怎麼辦呢?bash

我立馬想到的是在定時器實時獲取當前服務器時間,但這樣確定不行,服務器時間是經過接口獲取的,這樣就會大量形成服務器的壓力。服務器

後來想到能夠監聽瀏覽器先後臺的切換狀態,這樣就能夠可用這個狀態取去作一些事情了,好比切換回來的時候從新請求一下接口獲取服務器時間再作倒計時,這樣既能大大減小了服務器的壓力,又能獲得準確的服務器時間,從而讓倒計時變得準確無誤。微信

代碼以下:數據結構

mounted() {
   let _this = this;
   document.addEventListener("visibilitychange", _this.checkViChange); 
},
 
methods: {
    checkViChange() {
       if (!document.hidden) {
          this.getData();   //從新調用getData 方法去請求數據
       }
    }
}

複製代碼

這樣就能很好的捕抓瀏覽器的行爲去拿數據了。app

至少在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秒。

或許有人會說,本機系統時間是能夠隨便調的,就會致使差值不許確。其實這個問題是不存在的,由於你要調本機時間的時候你已經切到後臺運行了,再回來就是走前面提到的切換先後臺的邏輯了。

到這裏這個倒計時的問題就徹底解決了。因爲個人代碼涉及到多個倒計時而且數據結構比較複雜,因此就不貼出這部分代碼了。省得誤導。

相關文章
相關標籤/搜索