想必大家都遇到過瀏覽器頁面最小化、非當前聚焦頁面(鎖屏暫時沒有嘗試,按理是一樣的)這些場景時,再次打開目標頁面時,頁面自動刷新了一下,或者重新加載了一次,明顯可以看到卡頓了一下等等現象(不同瀏覽器策略有所不同)。體會最明顯的就是比如我們寫了一個時鐘的頁面,最小化等操作之前時間和機器時間是同步的,但是過了一會兒切回來的時候,時間就比機器時間慢了。
有很多大佬也都分析對比過具體的變化規律,比如速度變慢多少,離開多長時間開始,timeout、interval等等不同的命令的表現差異,這裏不再贅述。原因自然是系統電源選項爲了節省耗電以及節省內存空間將頁面假死閒置的一種策略。那麼解決方法目前可行的就是webworker了。
我們都知道webworker特性是爲了實現多線程的概念而產生的,但是實際應用很有限。而應用的方法也很簡單,引入一個js文件即可,消息傳遞和停止worker也都很簡單,示例:
1 // main.js(主線程) 2 3 const myWorker = new Worker('/worker.js'); // 創建worker 4 5 myWorker.addEventListener('message', e => { // 接收消息 6 console.log(e.data); // Greeting from Worker.js,worker線程發送的消息 7 }); 8 9 10 myWorker.postMessage('Greeting from Main.js'); // 向 worker 線程發送消息,對應 worker 線程中的 e.data 11 12 // worker.js(worker線程) 13 self.addEventListener('message', e => { // 接收到消息 14 console.log(e.data); // Greeting from Main.js,主線程發送的消息 15 self.postMessage('Greeting from Worker.js'); // 向主線程發送消息 16 }); 17 18 19 // 參考鏈接:https://juejin.cn/post/7139718200177983524
具體用法不再贅述。這裏需要強調的是,正常的使用時都需要一個單獨的js文件,而如果我們想在控制檯執行或者不想單獨增加文件,就可以使用下邊的方法:
1 // 文件名爲main.js 2 function work () { 3 onmessage = ({data: {message}}) => { 4 console.log ('i am worker, receive:' + message); 5 postMessage ({result: 'message from worker'}); 6 }; 7 } 8 9 const runWorker = f => { 10 const worker = new Worker ( 11 URL.createObjectURL (new Blob ([`(${f.toString ()})()`])) 12 ); 13 14 worker.onmessage = ({data: {result}}) => { 15 console.log ('i am main thread, receive:' + result); 16 }; 17 18 worker.postMessage ({message: 'message from main thread'}); 19 }; 20 21 const testWorker = runWorker (work); 22 23 24 25 26 // 用Promise和閉包的方式去改造 27 // 我們再讓它更通用一些,用Promise和閉包的方式去改造它,把runworker函數改造成一個makeworker函數 28 // 文件名爲main.js 29 function work () { 30 onmessage = ({data: {jobId, message}}) => { 31 console.log ('i am worker, receive:-----' + message); 32 postMessage ({jobId, result: 'message from worker'}); 33 }; 34 } 35 36 const makeWorker = f => { 37 let pendingJobs = {}; 38 39 const worker = new Worker ( 40 URL.createObjectURL (new Blob ([`(${f.toString ()})()`])) 41 ); 42 43 worker.onmessage = ({data: {result, jobId}}) => { 44 // 調用resolve,改變Promise狀態 45 pendingJobs[jobId] (result); 46 // 刪掉,防止key衝突 47 delete pendingJobs[jobId]; 48 }; 49 50 return (...message) => 51 new Promise (resolve => { 52 const jobId = String (Math.random ()); 53 pendingJobs[jobId] = resolve; 54 worker.postMessage ({jobId, message}); 55 }); 56 }; 57 58 const testWorker = makeWorker (work); 59 60 testWorker ('message from main thread').then (message => { 61 console.log ('i am main thread, i receive:-----' + message); 62 }); 63 // 參考鏈接https://www.lmlphp.com/user/16516/article/item/585417/