Appearance
web worker 多线程
一个 worker 是使用一个构造函数创建的一个对象,参数可以是远程 JavaScript 脚本(必须遵守同源策略),这也可以是一个 Blob 数据; worker 运行在另一个全局上下文中,不同于当前的 window,因此在 Worker 内通过 window 获取全局作用域将返回错误。
一个专用 worker 仅仅能被首次生成它的脚本使用,一个共享 worker 可以被多个脚本使用,即使这些脚本正在被不同的 window、iframe 或者 worker 访问。
页面与 worker 不会共享同一个实例,最终的结果就是在每次通信结束时生成了数据的一个副本。
worker 中可用的函数和接口
- Navigator
- XMLHttpRequest
- Array, Date, Math, and String
- setTimeout and setInterval
在一个 worker 中最主要不能做的事情就是直接影响父页面。包括操作父页面的节点以及使用页面中的对象。
专用 worker
js
// 主线程中
const worker = new Worker('worker.js')
worker.postMessage('主线程发送的消息')
worker.onmessage = function (e) {
console.log(e)
}
// worker.js
onmessage = function (e) {
postMessage('worker发送的消息')
}
有时 worker 处理需要根据内容定义,上面这种方式显然不合适,此时可以使用 Blob URL
作为参数。
js
const msg = '自定义消息'
const blo = new Blob(
[
`onmessage = function (data) {
console.log(data);
this.postMessage('${msg}');
};`
],
{ type: 'text/javascript' }
)
const url = URL.createObjectURL(blo)
const worker = new Worker(url)
worker.postMessage('主线程发送的消息')
worker.onmessage = function (data) {
console.log(data)
}
终止 worker
js
// 主线线程
worker.terminate()
// worker.js
close()
处理错误
js
worker.onerror = function (e) {
e.message // 错误信息
e.filename // 脚本名
e.lineno // 错误行号
}
引入脚本与库
importScripts 函数接受 0 个或者多个 URI 作为参数来引入资源。浏览器加载并运行每一个列出的脚本。每个脚本中的全局对象都能够被 worker 使用。
脚本的下载顺序不固定,但执行时会按照传入 importScripts() 中的文件名顺序进行。这个过程是同步完成的;直到所有脚本都下载并运行完毕,importScripts() 才会返回。
js
importScripts()
importScripts('a.js')
importScripts('a.js', 'b.js')
共享 worker
共享 worker 可以被多个浏览上下文调用,所有这些浏览上下文必须属于同源(相同的协议,主机和端口号)。
在传递消息之前,端口连接必须被显式的打开,打开方式是使用 onmessage 事件处理函数或者 start 方法。start 方法的调用只在一种情况下需要,那就是消息事件被 addEventListener() 方法注册。父级线程和 worker 线程需要双向通信,那么它们都需要调用 start() 方法。
当一个端口连接被创建时,worker 内使用 onconnect 事件处理函数来执。
js
// 主线程
const sWorker = new SharedWorker('worker.js')
sWorker.port.onmessage = function () {} // 不需要 start
sWorker.port.addEventListener('message', function () {}) // 需要
sWorker.port.start()
// worker.js
onconnect = function (e) {
const port = e.ports[0]
port.onmessage = function () {} // 不需要 start
port.addEventListener('message', function () {}) // 需要
port.start()
}
使用场景
- 计算量庞大
- worker 中可以通过 importScript(url)来加载其他文件
- 可以使用 XMLHttpRequest 来发送请求
局限性
- 同源限制,分配给 worker 线程的文件,必须与主线程同源
- DOM 限制,子线程中无法访问主线程所在网页的 dom 对象,无法使用 document、window、parent 等对象
- 通信限制,主线程和子线程必须通过 postMessage 进行通信
- 脚本限制,worker 线程中不允许使用 alert()和 confirm()
- 文件限制,worker 线程无法读取本地文件,只能是来自网络的文件
严格来讲这些线程并没有完整的功能,也因此这项技术并非改变了 javascript 语言的单线程本质。