PC 上的 Firefox、Chrome 和 Safari 等瀏覽器,都會自動把未激活頁面中的 JavaScript 定時器(setTimeout、setInterval)間隔最小值改為 1 秒以上;而移動設備上的瀏覽器往往會直接凍結未激活頁面上的所有定時器」。今天繼續聊一聊 JavaScript 定時器與移動 Web 這個話題。
計時器
最簡單的計時器只需要一個時間變量和固定間隔運行的函數就可以了,定期把上一次時間(默認為系統初始時間)加上運行間隔就是當前時間了。在 PC 上,這樣實現的計時器只要運行間隔沒有小於 1s,多半沒什么大問題。但移動端不一樣,只要頁面不在前台顯示,計時器就徹底不走了。
這個問題很容易解決,只要把定時器函數里的時間累加邏輯,換成每次都從系統時間獲取就可以了。那如果系統時間不准怎么辦?實際上大部分智能手機,默認都會開啟網絡校時,而且很多人有拿手機當手表的習慣,所以移動 Web 里獲取到的系統時間,相比 PC 端,要准確得多。
更嚴謹的做法是服務端輸出頁面時帶上服務器時間,JS 計時器先算出這個時間與系統時間之差 Δt,之后每次都用當前系統時間 + Δt 來還原服務器時間。邏輯很簡單不多介紹了,說兩個問題:
1)如果定時器運行時,用戶改了系統時間怎么辦?這個問題有很多解決方案,但是在移動設備上卻有個偷懶的辦法,由於移動設備上修改系統時間幾乎一定會讓頁面進入后台,所以我們只需要在頁面「從后台切回」時刷新下頁面就好了。
2)移動設備在 2G 等網絡環境下,接收響應需要更長時間,很有可能 JS 從頁面上拿到服務器時間,已經是幾十秒之后的事情了,喪失了准確性。如果一定要解決這個問題,使用 HTML5 新增的 Navigation Timing API 來修正是一種思路。美中不足的是 Android 4.0+ 才支持,Safari 系列則從未支持過這個 API。
從后台切回
要判斷移動端頁面是否從后台切回有很多方案,但利用后台頁面定時器被凍結這個特性,有個簡便且通用的做法:
var lastTime = +new Date; setInterval(function() { if(Math.abs(+new Date - lastTime) > 3000) { alert('從后台切回!'); } lastTime = +new Date; }, 1000);
原理很簡單,每隔一段時間(如 1s)更新頁面上某個變量,如果下次這個變量與當前系統時間之差大於某個閾值(如 3s),說明頁面定時器肯定被凍結過,也就是說頁面是從后台切回來。代碼中加上 Math.abs 是因為用戶可能把系統時間往回改(當然極端情況下還是會檢測不出來,忽略就好了)。
另一種定時器
「不會被 iOS 停掉的網頁定時器」,原理是利用不會被 iOS 凍結的<meta>
標簽 refresh 功能模擬 JS 定時器,有興趣的同學可以點這里了解下。
本文鏈接:https://www.imququ.com/post/mobile_web_and_js_timer.html
此文轉載自:JerryQu 的小站