Javascript:Web Worker基础

参考资料
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的方法有两个。

  1. 在外部执行worker.terminate()
  2. 在worker内部执行self.close()
    第二种方法比较好,因为这样可以防止意外关闭正在运行的worker

四 Worker的功能

由于Web Worker是多线程行为,所以功能是受限的,比如,它不能操作DOM。
Web Worker可以使用的功能有:

  1. navigator对象

  2. location对象(只读)

  3. XMLHttpRequest

  4. setTimeout() / clearTimeout() 和 setInterval() / clearInterval()

  5. Application Cache (AppCache)

  6. 使用 importScripts() 方法导入外部脚本
    importScripts('script1.js', 'script2.js'......);

  7. 生成其他Web Worker
    Worker 可以生成子 Worker。这对于在运行时进一步拆分大任务来说非常重要。但是,子 Worker 还有几点注意事项:

    • 子 Worker 必须托管在与父网页相同的来源中。
    • 子 Worker 中的 URI 应相对于父 Worker 的位置进行解析(与主网页不同)。

 
Web Worker不可以使用的功能:

  1. DOM
  2. window对象
  3. document对象
  4. parent对象

五 错误处理

如果在执行 Worker 时出现错误,就会触发 ErrorEvent。
三个可以帮你找出错误的实用属性:

  1. filename - 导致错误的 Worker 脚本的名称
  2. lineno - 出现错误的行号
  3. 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)
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容