在 Main Thread
可以使用 new Worker()
连接到指定 worker
。
而在 Worker Thread
中,顶级 self
对象指向 DedicatedWorkerGlobalScope
,即专用 worker
全局作用域。
3-1.Main Thread
// Main Thread
const worker = new Worker('./worker.js', {
type: 'module'
})
worker.postMessage('Post Message from Main Thread')
worker.onmessage = (event) => {
console.log('worker.onmessage', event)
}
worker.onmessageerror = (event) => {
console.log('worker.onmessageerror', event)
}
worker.onerror = (event) => {
console.log('worker.onerror', event)
}
1.创建worker
const worker = new Worker(url, options)
url
: 表示可执行脚本文件地址(必须是same-origin
)或者Base64
地址。options
:type
:classic
(默认)、module
(以esmodule
解析脚本)。credentials
:omit
(默认)、same-origin
、include
name
:自定义名字
TIP
url
可以是绝对路径或者相对路径,但也可以是 Base64
地址(MIME
类型是 application/javascript
等可解析形式)。
const worker = new Worker('./worker.js')
const customWorker = `
self.addEventListener('message', e => {
console.log(e.data)
})
`
const worker = new Worker(`data:application/javascript,${encodeURIComponent(customrWorker)}`)
2.实例方法
postMessage(message, transfer)
message
:
该
mesage
需要支持被结构化克隆算法处理,会被分发到event.data
中transfer
:
一个可选的、会被转移所有权的可转移对象数组。
如果一个对象的所有权被转移,它将在发送它的上下文中变为不可用(中止),而仅在接收方的 worker 中可用。
像
ArrayBuffer
、MessagePort
或ImageBitmap
类的实例才是可转移对象,才能够被转移。不能将null
作为transfer
的值。See Transfer Example.
terminate()
在 Main Thread
中断 worker
。
const worker = new Worker(url, options)
worker.terminate()
TIP
在 Main Thread
中断 worker
的话,那么 Main Thread
将不会再接收 worker
线程中的异步结果(无论是微任务还是宏任务)。
3.事件
message
Main thread
接收来自 worker thread
中利用 postMessage
传递的信息。
messageerror
当 worker
接收到一条无法被反序列化的消息时触发。
error
当 worker
发生错误时触发。
SecurityError
:脚本地址不合法或者不满足same-origin
同源策略;NetworkError
:脚本的MIME
类型不正确,可以是text/javascript
等其他可解析类型;SyntaxError
:脚本语法错误。
3-2.Worker Thread
// Worker Thread
self.addEventListener('message', (event) => {
console.log('dedecated.onmessage', event)
})
self.postMessage('Post Message from Worker Thread')
此作用域下,self
和 this
均指向 DedicatedWorkerGlobalScope
。
1.实例属性
name
在 Main Thread
中利用 new Worker(url, options)
创建时,可使用 options.name
进行定义。
2.实例方法
postMessage(message, transfer)
close()
在
Worker Thread
中断worker
。TIP
在
Worker Thread
中断worker
的话,那么Worker Thread
会中止宏任务队列,不会中止微任务队列。
3.事件
message
messageerror
3-3.中断与任务队列
在前文中,我们已经提到,在 Main Thread
中断 worker
与 Worker Thread
中断 worker
表现并不一致。
主要逻辑为:
TIP
在 Main Thread
中断 worker
的话,那么 Main Thread
将不会再接收 worker
线程中的异步结果(无论是微任务还是宏任务)。
在 Worker Thread
中断 worker
的话,那么 Worker Thread
会中止宏任务队列,不会中止微任务队列。
可以利用以下的代码进行测试:
// Main Thread
const worker = new Worker('./worker.js')
worker.addEventListener('message', (event) => {
console.log('Listen Message from Main Thread')
// worker.terminate()
})
worker.postMessage('Post Message from Main Thread')
// Worker Thread
self.addEventListener('message', (event) => {
// self.close()
console.log('Listen Message from Dedicated Worker')
// timeout
setTimeout(() => {
self.postMessage('Timeout Post Message from Dedicated Worker')
})
// promise
Promise.resolve().then(res => {
self.postMessage('Promise Post Message from Dedicated Worker')
Promise.resolve().then(res => {
self.postMessage('Promise2 Post Message from Dedicated Worker')
})
setTimeout(() => {
self.postMessage('Timeout2 Post Message from Dedicated Worker')
})
})
// loop
for (let i = 0;i <= 10000; i++) {
if (i === 10000) {
self.postMessage('Loop Post Message from Dedicated Worker')
}
}
})
3-4.importScripts()
在现实开发中,所有逻辑只放在一个 worker
文件内,明显是不便的。
因此,WorkGlobalScope
提供了一个 importScripts
方法,可以在 Worker Thread
中引入其他 worker
文件,从而方便开发者拆分 worker
模块。
我们假设有 Main Thread
、Worker Thread
以及 Sub Worker Thread
,那么可以这样使用 importScripts
:
// Main Thread
const worker = new Worker('./worker.js', {
type: 'classic'
})
worker.postMessage('Post Message from Main Thread')
worker.onmessage = (event) => {
console.log('worker.onmessage', event)
}
worker.onmessageerror = (event) => {
console.log('worker.onmessageerror', event)
}
worker.onerror = (event) => {
console.log('worker.onerror', event)
}
// Worker Thread
importScripts('./subWorker.js')
self.addEventListener('message', (event) => {
console.log('dedecated.onmessage', event)
})
getDefaultImportedMessage()
// Sub Worker Thread
function getDefaultImportedMessage () {
const message = 'Default imported message from Sub Worker Thread'
console.log(message)
return message
}
如果本例中要引入的 Sub Worker Thread
是 esModule
规范,那么需要改造以下几点:
Main Thread
中的type
类型声明为module
;Worker Thread
使用esModule
规范导入对应文件,不支持importScripts
导入。Module scripts don't support importScripts()
;Sub Worker Thread
中使用esModule
规范导出模块。
// Main Thread
const worker = new Worker('./worker.js', {
type: 'module'
})
worker.postMessage('postMessage from main thread')
worker.onmessage = (event) => {
console.log('worker.onmessage', event)
}
worker.onmessageerror = (event) => {
console.log('worker.onmessageerror', event)
}
worker.onerror = (event) => {
console.log('worker.onerror', event)
}
// Worker Thread
import getDefaultImportedMessage from './subWorker.js'
self.addEventListener('message', (event) => {
console.log('dedecated.onmessage', event)
})
getDefaultImportedMessage()
// Sub Worker Thread
function getDefaultImportedMessage () {
const message = 'Default imported message from Sub Worker Thread'
console.log(message)
return message
}
export {
getDefaultImportedMessage as default
}
非常重要的一点:
无论是 importScripts()
还是 esModule
,本例中的 Sub Worker Thread
的全局域是 DedicatedWorkerGlobalScope
,而非 SharedWorkerGlobalWorker
。
TIP
关于 importScripts
有如下特点:
引入的子
worker
全局域取决于该importScripts
的执行全局域。如果在DedicatedWorkerGlobalScope
中引入另一worker
文件,则该worker
文件的全局域是DedicatedWorkerGlobalScope
;如果在ServiceWorkerGlobalScope
中引入另一worker
文件,则该worker
文件的全局域是ServiceWorkerGlobalScope
。importScripts
存在变量提升。即使将importScripts()
方法放在代码末尾执行,实际结果也是importScripts()
方法内的文件先执行。