参考资料
Web Workers
web worker详解
Blob对象
Web Worker分为专属Worker(Dedicated Worker)和共享Worker(Shared Worker)。
专属Worker与页面是一对一的关系,它们直接链接到对应的页面。
共享Worker可以由多个窗口共享,只要这些窗口和该共享worder同源。共享Worker通过处于活动中的端口和主线程们进行通信。
本质上专属Worker也是通过端口和主线程通信的,但是因为双方端口是一致的,所以把这个特性隐藏了。
本文只讨论专属Worker
一 Web Worker是什么
我们知道js是单线程的,这是浏览器的特性决定的,这意味着我们不能用js进行高复杂度的运算,因为很可能会让浏览器进入“假死”状态。
Web Worker就是来解决这个问题的。它在浏览器后台运行js代码,而不会占用网页的主线程,从而提高网页的性能。
二 和主线程的通信
既然Web Worker是一个线程,那么它跟主线程之间必定需要进行线程间通信。
Web Worker使用postMessage()
方法进行通信。看下面的例子:
// index.js
var worker = new Worker('/worker.js')
worker.addEventListener('message', function (e) {
console.log('Worker said:', e.data)
}, false)
worker.postMessage('Hello World') // 把'Hello World'发送给worker
// worker.js
self.addEventListener('message', function (e) {
self.postMessage(e.data); // 把'Hello World'发送回给主线程
}, false)
// addEventListener也可以用onmessage来代替
// onmessage = function(e) {
// var data = e.data;
// ...
// };
如果不需要传递信息,只想要启动worker,可以不发送信息:
worker.postMessage() // 启动worker.
需要注意的是,通过postMessage(message)
传递的message并不是两个线程共享的,而是复制一个副本。
比如,现在我们传递一个对象:
// index.js
var worker = new Worker('/worker.js')
worker.addEventListener('message', function (e) {
console.log('Worker said:', e.data.a)
console.log('a of msg is:', msg.a)
}, false)
var msg = {
a: 2
}
worker.postMessage(msg)
// Worker.js
self.addEventListener('message', function (e) {
e.data.a = 3 // 修改传过来的msg中的a
self.postMessage(e.data);
}, false)
输出是这样的:
Worker said: 3
a of msg is: 2
说明两个线程的msg并没有指向同一个对象。
三 关闭worker
如果worker已经完成了工作,那么就可以关闭这个worker,释放资源了。
关闭worker的方法有两个。
- 在外部执行
worker.terminate()
- 在worker内部执行
self.close()
第二种方法比较好,因为这样可以防止意外关闭正在运行的worker
四 Worker的功能
由于Web Worker是多线程行为,所以功能是受限的,比如,它不能操作DOM。
Web Worker可以使用的功能有:
-
setTimeout() / clearTimeout() 和 setInterval() / clearInterval()
-
使用 importScripts() 方法导入外部脚本
importScripts('script1.js', 'script2.js'......);
-
生成其他Web Worker
Worker 可以生成子 Worker。这对于在运行时进一步拆分大任务来说非常重要。但是,子 Worker 还有几点注意事项:- 子 Worker 必须托管在与父网页相同的来源中。
- 子 Worker 中的 URI 应相对于父 Worker 的位置进行解析(与主网页不同)。
Web Worker不可以使用的功能:
- DOM
- window对象
- document对象
- parent对象
五 错误处理
如果在执行 Worker 时出现错误,就会触发 ErrorEvent。
三个可以帮你找出错误的实用属性:
- filename - 导致错误的 Worker 脚本的名称
- lineno - 出现错误的行号
- message - 有关错误的实用说明。
可以这样处理错误:
<!-- index.html -->
<output id="error" style="color: red;"></output>
<output id="result"></output>
<script>
function onError(e) { // 处理错误
document.getElementById('error').textContent = [
'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message].join('');
}
function onMsg(e) { // 处理信息
document.getElementById('result').textContent = e.data;
}
var worker = new Worker('workerWithError.js');
worker.addEventListener('message', onMsg, false);
worker.addEventListener('error', onError, false);
worker.postMessage(); // 启动Worker
</script>
// workerWithError.js
self.addEventListener('message', function(e) {
postMessage(1/x); // x没有定义,抛出错误
};
六 安全限制
1. Chrome本地访问限制
由于 Google Chrome 浏览器的安全限制,Worker 无法在最新版浏览器中本地运行(例如通过 file://)。
要通过 file:// 方案运行您的应用,请使用 --allow-file-access-from-files 标记设置来运行 Chrome 浏览器。
请注意:不推荐使用此标记设置来运行您的主浏览器。此标记设置仅供测试用,请勿用于常规浏览。
其他浏览器不存在相同的限制。
2. 同源限制
只能调用同源的Worker,即host相同,协议相同
七 Worker可以做什么
计算密集型和I/O密集型的操作都很适合用Web Worker来完成:
- 预先抓取和/或缓存数据以便稍后使用
- 突出显示代码语法或其他实时文本格式
- 拼写检查程序
- 分析视频或音频数据
- 背景 I/O 或网络服务轮询
- 处理较大数组或超大 JSON 响应
- <canvas> 中的图片过滤
- 更新本地网络数据库中的多行内容
......
深入了解Web Worker请戳这篇: web worker详解
八 内联Worker
有时候没有必要为一个Worker单独创建一个文件,那么可以使用Blob对象来写成内联的Worker:
// 该blob的内容为创建的worker的代码
var blob = new Blob([ "onmessage = function(e) { postMessage('msg from worker'); }"])
// 创建一个url指向该blob
var blobURL = window.URL.createObjectURL(blob)
var worker = new Worker(blobURL) // 创建一个worker,Worker接受一个url作为参数
worker.onmessage = function (e) {
console.log(e.data)
}
worker.postMessage('inline worker!')
我们可以创建一个函数来专门把特性的函数转化为blob并返回一个url让我们使用:
function fn2workerURL (fn) {
var blob = new Blob(['(' + fn.toString() + ')()'], {
type: 'application/javascript'
})
return URL.createObjectURL(blob) // 返回一个 blob URL
}
function fnInWorker () {
onmessage = function (e) {
postMessage(e.data)
}
}
var worker = new Worker(fn2workerURL(fnInWorker))
worker.onmessage = function (e) {
console.log(e.data)
}
网友评论