Web Workers
學習使用 Web Workers 在背景中運行 JavaScript 代碼的方法。
- 介紹
- Web Workers 的瀏覽器支持
- 創建 Web Worker
- 與 Web Worker 通信
- Web Worker 的生命週期
- 在 Web Worker 中加載庫
- Web Worker 可用的 API
Introduction
JavaScript 是單線程的,無法同時並行運行。
這很好,因為我們不需要擔心並發編程中可能發生的一系列問題。
由於這個限制,JavaScript 代碼從一開始就被迫高效,否則用戶會有不好的體驗。昂貴的操作應該是異步的,以避免阻塞線程。
隨著 JavaScript 應用程序的需求越來越多,這在某些場景中開始成為一個問題。
Web Workers 在瀏覽器內引入了並行執行的可能性。
它們有幾個限制:
- 無法訪問 DOM:Window 對象和 Document 對象
- 它們可以通過消息與主 JavaScript 程序進行通信
- 它們需要從相同的源頭(域名、端口和協議)加載
- 如果使用文件協議(
file://
)提供頁面,它們就不能工作
Web Worker 的全局作用域是一個名為 WorkerGlobalScope
的對象,而不是在主線程中的 Window。
Browser support for Web Workers
非常好!
您可以使用以下代碼檢查是否支持 Web Workers:
1 | if (typeof Worker !== 'undefined') { |
Create a Web Worker
您可以通過初始化 Worker 對象並從相同的源頭加載 JavaScript 文件來創建一個 Web Worker:
1 | const worker = new Worker('worker.js') |
Communication with a Web Worker
有兩種主要的方法可以與 Web Worker 通信:
- Web Worker 對象提供的 postMessage API
- Channel Messaging API
使用 Web Worker 物件中的 postMessage
您可以在 Worker 對象上使用 postMessage
發送訊息。
重要提示:訊息是傳輸的,而不是共享的。
main.js
1
2 const worker = new Worker('worker.js')
worker.postMessage('hello')
worker.js
1
2
3
4
5
6
7 onmessage = event => {
console.log(event.data)
}
onerror = event => {
console.error(event.message)
}
傳回訊息
Worker 可以通過使用其全局的 postMessage()
函數將訊息傳回到創建它的函數中。
worker.js
1
2
3
4
5
6
7
8 onmessage = event => {
console.log(event.data)
postMessage('hey')
}
onerror = event => {
console.error(event.message)
}
main.js
1
2
3
4
5
6 const worker = new Worker('worker.js')
worker.postMessage('hello')
worker.onmessage = event => {
console.log(event.data)
}
多個事件監聽器
如果您想為 message
事件設置多個監聽器,而不是使用 onmessage
創建一個事件監聽器(適用於 error
事件),則可以使用 addEventListener
:
worker.js
1
2
3
4
5
6
7
8
9
10
11
12 addEventListener('message', event => {
console.log(event.data)
postMessage('hey')
}, false)
addEventListener('message', event => {
console.log(`I'm curious and I'm listening too`)
}, false)
addEventListener('error', event => {
console.log(event.message)
}, false)
main.js
1
2
3
4
5
6 const worker = new Worker('worker.js')
worker.postMessage('hello')
worker.addEventListener('message', event => {
console.log(event.data)
}, false)
使用 Channel Messaging API
與使用內建的 postMessage API 提供的 Web Workers 不同,我們可以選擇使用更通用的 Channel Messaging API 來進行通信。
main.js
1
2
3
4
5
6 const worker = new Worker('worker.js')
const messageChannel = new MessageChannel()
messageChannel.port1.addEventListener('message', event => {
console.log(event.data)
})
worker.postMessage(data, [messageChannel.port2])
worker.js
1
2
3 addEventListener('message', event => {
console.log(event.data)
})
Web Worker 可以通過將消息發送到 messageChannel.port2
來傳回訊息,例如:
1 | addEventListener('message', event => { |
Web Worker Lifecycle
如果 Web Worker 在 worker.onmessage
或添加事件監聽器後不保持在接聽模式下,那麼當其代碼執行完成時,它們將被關閉。
Web Worker 可以通過主線程的 terminate()
方法停止,在 Worker 內部可以使用全局方法 close()
:
main.js
1
2
3 const worker = new Worker('worker.js')
worker.postMessage('hello')
worker.terminate()
worker.js
1
2
3
4
5
6
7
8 worker.onmessage = event => {
console.log(event.data)
close()
}
worker.onerror = event => {
console.error(event.message)
}
Loading libraries in a Web Worker
Web Worker 可以使用其全局作用域中定義的 importScripts()
全局函數:
1 | importScripts('../utils/file.js', './something.js') |
Web Worker 可用的 API
如前所述,Web Worker 無法訪問 DOM,因此無法與 window
和 document
對象進行交互,而 parent
也無法使用。
但是,您仍然可以使用許多其他 API,其中包括:
- XHR API
- Fetch API
- BroadcastChannel API
- FileReader API
- IndexedDB
- Notifications API
- Promises
- Service Workers
- Channel Messaging API
- Cache API
- Console API(
console.log()
等) - JavaScript Timers(
setTimeout
、setInterval
等) - CustomEvents API:
addEventListener()
和removeEventListener()
- 目前的 URL,可以通過讀取模式下的
location
屬性訪問 - WebSockets
- WebGL
- SVG Animations
tags: [“JavaScript”, “Web Workers”, “parallel execution”, “messaging”, “Channel Messaging API”, “DOM”, “APIs”]