让Web Worker来给你的网页提提速

前言

大家好,我是辉夜真是太可爱啦

我们都知道,现在的 CPU 都是多核的,性能都很高。

但是, JS 语言采用的是单线程模型。至于原因可以查阅 单线程的JS

也就是说,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着,根本无法发挥现在多核 CPU 的优势。

如何另起线程

如果我们想在 JS 单线程的基础上,额外创建一个子进程,那么,我们就需要用到 Web Worker

在主线程运行的同时,创建一个 Worker 子线程在后台运行,子线程可以执行任务而不干扰用户界面,待任务完成之后,将结果返回给主线程。

例如我们可以将复杂的运算放在 Worker 子线程中运行,子线程和主线程同时工作,待任务完成之后,将结果返回给主线程,主线程也不会因此而堵塞。

Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。

tips:异步任务就没必要放 Worker 子线程中运行了,因为按照 JS 运行机制,异步任务本身就不阻塞主线程的执行,等异步任务执行完之后,等主线程空闲之后,再依次执行异步任务队列。具体的执行机制可以查阅我这一篇文章 一文搞懂JS系列(六)之微任务与宏任务,Event Loop

局限性

1. 同源策略

这是由于浏览器的安全机制所导致的,分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源

2. DOM 限制

由于 Worker 是运行在另一个全局上下文中,不同于当前的 window ,所以,无法读取主线程所在网页的 DOM 对象,也无法使用 documentwindowparent 这些对象。但是,Worker 线程可以navigator对象和location对象,以及 WebSocketsIndexedDB 等功能。

3. 直接通信限制

由于 Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。

双方都使用 postMessage() 方法发送各自的消息,使用 onmessage 事件处理函数来响应消息,这个过程中数据并不是被共享而是被复制

4. 脚本限制

Worker 线程不能执行 alert() 方法和 confirm() 方法,但可以使用 XMLHttpRequest , Promise , Console 等特性。(具体可以查阅 这里

5. 文件限制

Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。

基本用法

image.png
image.png

特性检测

在开始使用之前,最好先进行兼容性的判断

if (window.Worker) {
  ...   // do something
}

创建实例

创建一个新的 Worker 很简单,你也可以叫他 专用 Worker 。你需要做的是调用 Worker() 构造函数,指定一个来自网络的脚本。(切记无法读取本地文件

let myWorker = new Worker('worker.js');

通信联系

主线程

发送数据

主线程调用 myWorker.postMessage() 向子线程发送信息。

myWorker.postMessage('Cool Dream');
myWorker.postMessage({name: 'huiYe', age: 26});

postMessage() 内的参数,就是要发送的数据,它可以是各种数据类型,包括二进制数据。

接收数据

主线程通过worker.onmessage指定监听函数,接收子线程发回来的消息。

myWorker.onmessage=(res)=>{
  console.log(res.data)
}

通过 res.data 可以获取到 Worker 子线程中发过来的数据。

Worker 子线程

在 Worker 子线程中,有一个 self 关键字,代表子线程本身。

可以通过 self.name 获取当前的子线程名字。

发送数据
self.postMessage('Cool Dream');
self.postMessage({name: 'huiYe', age: 26});
接收数据

主线程通过worker.onmessage指定监听函数,接收子线程发回来的消息。

self.onmessage=(res)=>{
  console.log(res.data)
}

通过 res.data 可以获取到主线程中发过来的数据。

也可以通过 self.addEventListener() 来获取数据。

self.addEventListener('message', res=>{
  console.log(res.data)
}, false);

错误处理

当 Worker 发生错误时,可以在主线程中通过 onerror 或者 addEventListener('error')监听到

myWorker.onerror((error)=>{
  console.log(error)
});

// 或者
myWorker.addEventListener('error', ()=>{
  // ...
});

资源关闭

使用完之后,一定要记得关闭它,避免多余的资源浪费

// 主线程
myWorker.terminate();

// Worker 线程
self.close();

进阶

Worker 加载脚本

有的时候,我们需要在 Worker 中加载一个或多个脚本,那么,就需要用到 importScripts

// 加载一个脚本
importScripts('script1.js');

// 加载多个脚本
importScripts('script1.js', 'script2.js');

浏览器加载并运行每一个 importScripts 引入的脚本。

每个脚本中的全局对象都能够被 Worker 使用。如果脚本无法加载,将抛出 NETWORK_ERROR 异常,接下来的代码也无法执行。而之前执行的代码(包括使用 window.setTimeout() 异步执行的代码)依然能够运行。

importScripts() 之后的函数声明依然会被保留,由于变量提升的缘故,所以它们始终会在其他代码之前运行。

可转让对象

在上面,我们提到了主线程和子线程的通信过程中,使用的 postMessage 可以传递各种类型的数据。

而这种通信的方式,是拷贝关系。当传递的值是引用类型的时候,传递的也是对象的值,而不是引用地址。所以,在 Worker 中对传递的内容进行修改也不会造成主线程的数据修改。

传递对象的值,当传递的对象足够庞大时,就会引发一个性能问题。

这种时候,可以使用 可转让对象 ,相当于让这个对象的引用从当前执行上下文转移到另一个执行上下文,当前执行上下文中的这个对象就会完全无法使用,有点资产转移的味道了。

Worker 中创建 Worker

对,你没听错,Worker 线程内部还能再新建 Worker 线程。

文章参考

MDN

Web Worker 使用教程

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

推荐阅读更多精彩内容