共计 2557 个字符,预计需要花费 7 分钟才能阅读完成。
浏览器中的 Web Worker
背景介绍
我们都知道 JavaScript 这个语言在执行的时候是采用单线程进行执行的,也就是说在同一时间只能做一件事,这也和这门语言有很大的关系,采用同步执行的方式进行运行,如果出现阻塞,那么后面的代码将不会执行,HTML5 则提出了 web Worker 标准,表示 JavaScript 允许有多个线程,但是子线程完全受主线程的控制,子线程不能操作 DOM,只有主线程可以操作 DOM,所以以主线程为主的单线程执行原理成了 JavaScript 这门语言的核心。
进程与线程的区别
根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位。在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行。
兼容性
web worker 是什么?
简单来说,其实就是在 Javascript 单线程执行的基础上,开启一个子线程,进行程序处理,而不影响主线程的执行,当子线程执行完毕之后再回到主线程上,在这个过程中并不影响主线程的执行过程。举个栗子传统情况下,执行下面的代码后,整个页面都会被冻结,由于 javascript 是单线程处理,如下代码已经完全组塞了后续的执行
while(1){}
如果换一种方式,我们通过开启一个新的线程来执行这段代码,将他放在一个单独的 worker.js 文件中, 在主线程执行以下代码,则可以避免这种情况。
var worker = new Worker(“worker.js”)
Web Worker 的用法
判断当前浏览器是否支持 web worker
if (typeof (Worker) != “undefined”) {// 浏览器支持 web worker
if (typeof (w) == “undefined”) {// w 是未定义的, 还没有开始计数
w = new Worker(“webworker.js”); // 创建一个 Worker 对象, 利用 Worker 的构造函数
}
//onmessage 是 Worker 对象的 properties
w.onmessage = function (event) {// 事件处理函数, 用来处理后端的 web worker 传递过来的消息
// do something
};
}
else {// 浏览器不支持 web worker
// do something
}
API
①创建新的 Worker
var worker = new Worker(“worker.js”)
②传递参数
worker.postMessage()
③接收消息
worker.onMessage = function(msg){}
④异常处理
worker.onerror = function(err){}
⑤结束 worker
worker.terminate()
⑥载入工具类函数
importScripts()
Worker 作用域
当我们创建一个新的 worker 时,该代码会运行在一个全新的 javascript 的环境中(WorkerGlobalScope)运行, 是完全和创建 worker 的脚本隔离,这时我们可以吧创建新 worker 的脚本叫做主线程,而被创建的新的 worker 叫做子线程。WorkerGlobalScope 是 worker 的全局对象,所以它包含所有核心 javascript 全局对象拥有的属性如 JSON 等,window 的一些属性,也拥有类似于 XMLHttpRequest() 等。但是我们所开启的新的 worker 也就是子线程,并不支持操作页面的 DOM。
线程间的通讯是传值而不是传地址
主线程与子线程数据通信方式有多种,通信内容,可以是文本,也可以是对象。需要注意的是,这种通信是拷贝关系,即是传值而不是地址,子线程对通信内容的修改,不会影响到主线程。事实上,浏览器内部的运行机制是,先将通信内容串行化,然后把串行化后的字符串发给子线程,后者再将它还原。
JavaScript 中的数据类型存放原理以及传递规则
共享线程(SharedWorker)
共享线程是为了避免线程的重复创建和销毁过程,降低了系统性能的消耗,共享线程 SharedWorker 可以同时有多个页面的线程链接。使用 SharedWorker 创建共享线程,也需要提供一个 javascript 脚本文件的 URL 地址或 Blob, 该脚本文件中包含了我们在线程中需要执行的代码,如下:
var worker = new SharedWorker(“sharedworker.js”);
共享线程也使用了 message 事件监听线程消息,但使用 SharedWorker 对象的 port 属性与线程通信如下:
worker.port.onmessage = function(msg){};
同时我们也可以使用 SharedWorker 对象的 port 属性向共享线程发送消息如下:
worker.port.postMessage(msg);
运行原理
生命周期
①当一个 web worker 的文档列表不为空的时候,这个 web worker 会被称之为许可线程。②当一个 web worker 的文档列表中的任何一个对象都是处于完全活动状态的时候,这个 web worker 会被称之为需要激活线程。③当一个 web worker 是许可线程并且拥有计数器或者拥有数据库事务或者拥有网络连接或者它的 web worker 列表不为空的时候,这个 web worker 会被称之为受保护的线程。④当一个 web worker 是一个非需要激活线程同时又是一个许可线程的时候,这个 web worker 会被称之为可挂起线程。
以 webKit 为例加载并执行 worker 的过程
应用
可以做什么:
1. 可以加载一个 JS 进行大量的复杂计算而不挂起主进程,并通过 postMessage,onmessage 进行通信 2. 可以在 worker 中通过 importScripts(url) 加载另外的脚本文件 3. 可以使用 setTimeout(), clearTimeout(), setInterval(), and clearInterval()4. 可以使用 XMLHttpRequest 来发送请求 5. 可以访问 navigator 的部分属性
不可以做什么:
1. 不能跨域加载 JS2.worker 内代码不能访问 DOM3. 各个浏览器对 Worker 的实现不大一致,例如 FF 里允许 worker 中创建新的 worker, 而 Chrome 中就不行 4. 不是每个浏览器都支持这个新特性