相關免費學習推薦:javascript(影片)
這次我們會逐步解釋Web Workers,先說個簡單的概念,接著討論不同類型的Web Workers,他們的組成部分是如何一起工作的,以及不同場景下它們各自優勢和限制。最後,提供5個正確使用 Web Workers 的場景。
正如我們前面文章討論的那樣,你應該知道 JavaScript 語言採用的是單線程模型。然而,JavaScript 也為開發人員提供了編寫非同步程式碼的機會。
先前的文章討論過非同步編程,以及應該在什麼時候使用它。
非同步程式設計可以讓UI介面是響應式(渲染速度快)的,透過"程式碼調度",讓需要請求時間的程式碼先放到在event loop中晚一點再執行,這樣就允許UI先行渲染展示。
非同步程式設計的一個很好的用例就 AJAX 請求。由於請求可能花費大量時間,因此可以使用非同步請求,在客戶端等待回應的同時也可以執行其他程式碼。
然而,這帶來了一個問題——請求是由瀏覽器的WEB API處理的,但是如何使其他程式碼是異步的呢?例如,如果成功回呼中的程式碼非常佔用CPU:
var result = performCPUIntensiveCalculation();
如果performCPUIntensiveCalculation
不是一個HTTP請求而是一個阻塞程式碼(例如一個內容很多的for loop循環),就沒有辦法及時清空事件循環,瀏覽器的UI 渲染就會被阻塞,頁面無法及時回應給使用者。
這意味著非同步函數只能解決一小部分 JavaScript 語言單執行緒中的限制問題。
在某些情況下,可以使用setTimeout
對長時間運行的計算阻塞的,可以使用setTimeout
暫時放入非同步佇列中,從讓頁面得到更快的渲染。例如,透過在單獨的setTimeout
呼叫中批次複雜的計算,可以將它們放在事件循環中單獨的「位置」上,這樣可以爭取為UI 渲染/回應的執行時間。
看一個簡單的函數,計算一個數字數組的平均值:
#以下是重寫上述程式碼並「模擬」非同步性的方法:
function averageAsync(numbers, callback) { var len = numbers.length, sum = 0; if (len === 0) { return 0; } function calculateSumAsync(i) { if (i <p>使用setTimeout函數,該函數將在事件循環中進一步新增計算的每個步驟。在每次計算之間,將有足夠的時間進行其他計算,從而可以讓瀏覽器進行渲染。 </p><h2>Web Worker 可以解決這個問題</h2><p>HTML5為我們帶來了很多新的東西,包括:</p>
Web Worker 的作用,就是為JavaScript 創造多線程環境,允許主執行緒創建Worker 線程,將一些任務分配給後者運行。在主執行緒運行的同時,Worker 執行緒在後台運行,兩者互不干擾。等到 Worker 執行緒完成計算任務,再把結果回傳給主執行緒。這樣的好處是,一些計算密集型或高延遲的任務,被 Worker 線程負擔了,主線程(通常負責 UI 交互)就會很流暢,不會被阻塞或拖慢。
你可能會問:「JavaScript不是一個單執行緒的語言嗎?」
事實上 JavaScript 是一種不定義執行緒模型的語言。 Web Workers 不是 JavaScript 的一部分,而是可以透過 JavaScript 存取的瀏覽器特性。歷史上,大多數瀏覽器都是單線程的(當然,這已經改變了),大多數 JavaScript 實作都入發生在瀏覽器中。 Web Workers 不是在 Node.JS 中實現的。 Node.js 中有類似的叢集(cluster)、子行程概念(child_process),他們也是多執行緒但是和 Web Workers 還是有差別 。
值得注意的是,規範中提到了三種類型的Web Workers:
專用Workers 只能被創建它的頁面訪問,並且只能與它通訊。以下是瀏覽器支援的情況:
共享 Workers 在同一源(origin)下面的各种进程都可以访问它,包括:iframes、浏览器中的不同tab页(一个tab页就是一个单独的进程,所以Shared Workers可以用来实现 tab 页之间的交流)、以及其他的共享 Workers。以下是浏览器支持的情况:
Service Worker 功能:
在目前阶段,Service Worker 的主要能力集中在网络代理和离线缓存上。具体的实现上,可以理解为 Service Worker 是一个能在网页关闭时仍然运行的 Web Worker。以下是浏览器支持的情况:
本文主要讨论 专用 Workers,没有特别声明的话,Web Workers、Workers都是指代的专用 Workers。
Web Workers 一般通过脚本为 .js
文件来构建,在页面中还通过了一些异步的 HTTP 请求,这些请求是完全被隐藏了的,你只需要调用 Web Worker API.
Worker 利用类线程间消息传递来实现并行性。它们保证界面的实时性、高性能和响应性呈现给用户。
Web Workers 在浏览器中的一个独立线程中运行。因此,它们执行的代码需要包含在一个单独的文件中。这一点很重要,请记住!
让我们看看基本 Workers 是如何创建的:
var worker = new Worker('task.js');
Worker()
构造函数的参数是一个脚本文件,该文件就是 Worker 线程所要执行的任务。由于 Worker 不能读取本地文件,所以这个脚本必须来自网络。如果下载没有成功(比如404错误),Worker 就会默默地失败。
为了启动创建的 Worker,需要调用 postMessage
方法:
worker.postMessage();
为了在 Web Worker 和创建它的页面之间进行通信,需要使用 postMessage
方法或 Broadcast Channel。
新浏览器支持JSON对象作为方法的第一个参数,而旧浏览器只支持字符串。
来看一个示例,通过将 JSON 对象作为一个更“复杂”的示例传递,创建 Worker 的页面如何与之通信。传递字符串跟传递对象的方式也是一样的。
让我们来看看下面的 HTML 页面(或者更准确地说是它的一部分):
<button>Start computation</button> <script> function startComputation() { worker.postMessage({'cmd': 'average', 'data': [1, 2, 3, 4]}); } var worker = new Worker('doWork.js'); worker.addEventListener('message', function(e) { console.log(e.data); }, false); </script>
然后这是 worker 中的 js 代码:
self.addEventListener('message', function(e) { var data = e.data; switch (data.cmd) { case 'average': var result = calculateAverage(data); // 从数值数组中计算平均值的函数 self.postMessage(result); break; default: self.postMessage('Unknown command'); } }, false);
当单击该按钮时,将从主页调用 postMessage
。postMessage 行将 JSON 对象传给Worker。Worker 通过定义的消息处理程序监听并处理该消息。
当消息到达时,实际的计算在worker中执行,而不会阻塞事件循环。Worker 检查传递的事件参数 e
,像执行 JavaScript 函数一样,处理完成后,把结果传回给主页。
在 Worker 作用域中,this 和 self 都指向 Worker 的全局作用域。
有两种方法可以停止 Worker:从主页调用worker.terminate()
或在worker
内部调用self.close()
。
Broadcast Channel API 允许同一原始域和用户代理下的所有窗口,iFrames 等进行交互。也就是说,如果用户打开了同一个网站的的两个标签窗口,如果网站内容发生了变化,那么两个窗口会同时得到更新通知。
还是不明白?就拿 Facebook 作为例子吧,假如你现在已经打开 了Facebook 的一个窗口,但是你此时还没有登录,此时你又打开另外一个窗口进行登录,那么你就可以通知其他窗口/标签页去告诉它们一个用户已经登录了并请求它们进行相应的页面更新。
// Connection to a broadcast channel var bc = new BroadcastChannel('test_channel'); // Example of sending of a simple message bc.postMessage('This is a test message.'); // Example of a simple event handler that only // logs the message to the console bc.onmessage = function (e) { console.log(e.data); } // Disconnect the channel bc.close()
可以从下面这张图,在视觉上来清晰地感受 Broadcast Channel:
Broadcast Channel 浏览器支持比较有限:
有两种方式发送消息给Web Workers:
由於 JavaScript的多執行緒特性,Web工作者只能存取JavaScript特性的子集。以下是它的一些特點:
Web Workers 由於具有多執行緒特性,因此只能存取 JavaScript 特性的子集。以下是可使用特性清單:
importScripts()
匯入外部腳本遺憾的是,Web Workers 無法存取一些非常關鍵的JavaScript 特性:
這表示Web Worker 不能操作DOM (因此也無法操作UI )。有時這可能很棘手,但是一旦你了解如何正確使用 Web Workers,你就會開始將它們作為單獨的「電腦」使用,而所有 UI 變更都會發生在你的頁面程式碼中。 Workers 將為你完成所有繁重的工作,然後一旦完成再把結果回傳給 page 頁面。
和 JavaScript 程式碼一樣,Web workers 裡拋出的錯誤,你也需要處理。當 Worker 執行過程中如果遇到錯誤,會觸發一個 ErrorEvent
事件。介麵包含了三個有用的屬性來幫忙排查問題:
##在這裡,可以看到我們建立了一個worker 並開始偵聽錯誤事件。
在worker 內部(在
workerWithError.js 中),我們透過將未定義x
乘以2 來建立一個異常。異常被傳播到初始腳本,然後透過頁面監聽 error事件,對錯誤進行捕獲。 5個好的 Web Workers 應用程式實例
Spell checking(拼字檢查):一個基本的拼字檢查程式的工作流程如下-程式讀取一個字典檔案與一個正確拼字單字清單。字典被解析為一個搜尋樹,以使實際的文字搜尋更有效。當一個單字被提供給檢查器時,程式檢查它是否存在於預先建立的搜尋樹中。如果在樹中沒有找到該單詞,可以透過替換替換字元並測試它是否是有效的單字(如果是使用者想要寫的單字),為使用者提供替代拼字。所有的這些處理過程都可以在 Web Worker中進行了,使用者可以不被阻塞的輸入詞彙和句子,Web Worker 在後台校驗詞彙是否正確以及提供備選詞彙。
想了解更多程式設計學習,請關注php培訓##欄位!
以上是JavaScript Web Workers的建構塊及5個使用場景的詳細內容。更多資訊請關注PHP中文網其他相關文章!