聊聊 Web Workers 吧

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"背景","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲啥突然想學習 Web Worker 了呢,因爲我在某金上看到了一篇文章,而那篇文章有個評論說 Web Worker 實現起來很絲滑,我在想是要怎麼實現呢,於是就開啓了探究 Web Worker 之旅……","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"閱讀 MDN","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以下是 MDN 的相關解釋:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用Web Workers,Web應用程序可以在獨立於主線程的後臺線程中,運行一個腳本操作。這樣做的好處是可以在獨立線程中執行費時的處理任務,從而允許主線程(通常是UI線程)不會因此被阻塞/放慢。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"顯然使用 Web Worker 是有好處的,就是可以將任務重的計算在不阻塞主線程的情況下繼續運行,實現性能的提升(不過也不宜過度使用,後面會說到)。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Web Worker 可分爲 Dedicated Workers、Shared Workers、Service Workers、Chrome Workers以及音頻Workers,後面兩個我負責的項目中使用場景不多,就不展開了,主要介紹前三個 Web Workers。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Dedicated Workers","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"定義、使用","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Dedicated Workers 使用構造函數 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Worker()","attrs":{}}],"attrs":{}},{"type":"text","text":" 創建一個Worker對象,構造函數接受一個 JavaScript文件URL,這個文件包含了將在 worker 線程中運行的代碼,具體例子:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"js"},"content":[{"type":"text","text":"// main.js\nvar myWorker = new Worker('./worker.js');\n\n// worker.js,裏面是worker線程運行的任務(執行的計算比較重的代碼)\nonmessage = function(e) {\n console.log('Message received from main script');\n var workerResult = 'Result: ' + (e.data[0] * e.data[1]);\n console.log('Posting message back to main script');\n postMessage(workerResult);\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"啓動/運行 Web Worker","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先要知道 Worker 線程無法讀取本地文件,即不能打開本機的文件系統(file://),它所加載的腳本,必須來自網絡。所以如果本地創建了一個 html 文件和 js 文件,直接在瀏覽器打開該 html 文件,Web Worker 是無效的,比如我打開的是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"file:///Users/xxx/Desktop/aaa/aaa.html","attrs":{}}],"attrs":{}},{"type":"text","text":",內部引入的js包含了上面 Web Worker 相關的 JS 代碼,那麼控制檯會報出一個錯誤","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/be/be5b6cd243831ffe98a6e7ab9e5c41ff.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以如果想在本地調試,需要將文件serve起來,我是用的是python命令:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"python -m SimpleHTTPServer 8000","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Chrome Debug","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼上實現了 Web Worker,也確實能正常運行,那麼我怎麼知道worker到底在哪裏呢,我的頁面裏面有幾個workers呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2c/2c495341e7304b0ccdb818370b6e727a.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"打開 Chrome -> CMD+SHIFT+I -> Sources Tab -> Page,然後就可以看到有多少個 Web Worker 以及具體 Web Worker 的腳本。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"關閉 Web Worker","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用完畢,爲了節省系統資源,必須關閉 Worker。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"js"},"content":[{"type":"text","text":"// 主線程\nworker.terminate();\n// Worker 線程\nself.close();\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於 Dedicated Workers而言,關閉意味着在 chrome 的sources tab也會消失:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a5/a5d6cc63780be43b23e1a606991faf74.gif","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Shared Workers","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Shared Workers 跟 Dedicated Workers使用上是基本一致的,可能更復雜一些,Shared Workers 可被不同的窗體的多個腳本運行,例如IFrames等。代碼舉例🌰:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"js"},"content":[{"type":"text","text":"// main.js\nif (!!window.SharedWorker) {\n var myWorker = new SharedWorker(\"worker.js\");\n\n first.onchange = function() {\n myWorker.port.postMessage([first.value, second.value]);\n console.log('Message posted to worker');\n }\n\n second.onchange = function() {\n myWorker.port.postMessage([first.value, second.value]);\n console.log('Message posted to worker');\n }\n\n myWorker.port.onmessage = function(e) {\n result1.textContent = e.data;\n console.log('Message received from worker');\n console.log(e.lastEventId);\n }\n}\n\n// worker.js\nonconnect = function(e) {\n var port = e.ports[0];\n port.onmessage = function(e) {\n var workerResult = 'Result: ' + (e.data[0] * e.data[1]);\n port.postMessage(workerResult);\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Chrome的debug方式也不一樣,打開","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"chrome://inspect","attrs":{}}],"attrs":{}},{"type":"text","text":",可以看到很多navigations,其中有一個叫做","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Shared Workers","attrs":{}}],"attrs":{}},{"type":"text","text":",那這裏是可以看到打開的網頁有哪些是使用了 Shared Workers的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1f/1ff555869a10a560b0801281484249a1.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Service Workers","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"說實話,我本人接觸的第一個Web Workers就是Service Worker,以前有一個項目是給工人在工地上做工時應用,工地也沒有網絡,或者說信號極差,所以對離線要求也比較高,當時調研過 Service Worker,但是使用起來有點複雜的,再加上當時不滿足瀏覽器的兼容性,所以就使用了另外一種方式","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"indexdb","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據文檔,Service Worker 可以創建有效的離線體驗,攔截網絡請求,以及根據網絡是否可用採取合適的行動,更新駐留在服務器上的資源。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Angular框架是實現了 Service Worker的,而其中也有一些bug(記得是版本7,現在有可能已經修復了,很久沒用過Angular了😅),如果開啓了之後,可能會對版本的更新有一定的影響,話說至今我都不明白爲啥那個項目要開啓Service Worker,大家也米有離線的需求額……","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Chrome瀏覽器也是可以很方便地debug,在Application tab的子菜單裏面:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d6/d6062910e6ebb01f04a347b160a40328.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"開發注意的地方/限制","attrs":{}}]},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"前面也提到了,Web Workers 不能通過本地文件的方式運行,只能通過網絡,否則無法執行;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"同源:分配給 Worker 線程運行的腳本文件,必須與主線程的腳本文件同源;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"DOM 限制:可能很多人特別開心,那頁面渲染的性能瓶頸是不是就能通過 Web Workers 來解決了呢,還是圖樣圖森破啊,Worker 線程所在的全局對象,與主線程不一樣,無法讀取主線程所在網頁的 DOM 對象,也無法使用document、window、parent這些對象。","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是還是有很多 Web API 在 Web Workers中是可以訪問的,比如使用 XMLHttpRequest 來訪問網絡,可以使用navigator對象和location對象等,所以別灰心,大多數 API 也是可以用的。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"思考","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然學習了 Web Workers,也知道如何使用,但是目前來講好像使用 Web Workers 解決問題的項目不多,通過搜索引擎發現很多庫/工具都實現了 Web Workers呢,我個人還是很看好 Web Workers 滴:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"🌟: ","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/alewin/useWorker","title":"","type":null},"content":[{"type":"text","text":"useWorker","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"🌟: ","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/andywer/threads.js","title":"","type":null},"content":[{"type":"text","text":"Threadjs","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"🌟: ","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/nitish24p/react-worker-image","title":"","type":null},"content":[{"type":"text","text":"react-worker-image","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"🌟: ","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/ampproject/worker-dom","title":"","type":null},"content":[{"type":"text","text":"worker-dom","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"歡迎大家一起交流哦🍺🍺🍺","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"References:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"http://www.ruanyifeng.com/blog/2018/07/web-worker.html","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Functions_and_classes_available_to_workers","attrs":{}}]}]}],"attrs":{}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章