HTML5新特性之线程
简单背景介绍
JavaScripts 本身是一种单线程设计,无法在同一时刻并行的运行多个脚本。浏览器处理的每一个任务都是通过串行的方式进行处理。
通常当使用setTimeout()和setInterval()这样的函数时,可能会产生多个线程独立于JavaScript主线程同时运行的错觉,但事实上,这些函数其实都被加入了主线程使用得同一个事件循环里。这种方式有一个缺点,一旦使用任何会导致阻塞的函数,就会使浏览器失去响应。
如Ajax中使用的XMLHttpRequest对象有同步和异步两种模式。异步模式使用的频率要远远高于同步模式,因为同步的请求会牵制整个线程,在请求返回前会阻塞所有后续指令的执行。这会导致所有与页面的交互行为全部失效,表现出来的效果就是页面虽然显示出来了,但是会没有反应。
Web Workers
Web Workers通过引入类似线程的机制是这种问题得到有效的解决。
使用Web Workers创建一个工作线程就是能够简单地加载一段脚本并在后台线程中执行。 一个运行在工作线程中的脚本是无法影响或阻塞主线程的,这意味着可以一边进行cpu密集型的处理,同时用户继续运行游戏或者应用。
分类
专用线程
- 专用型worker与创建它的脚本连接在一起,它可以与其他的worker或是浏览器组件通信,但是他不能与DOM通信。专用的含义,就是这个线程一次只处理一个需求。专用线程在除了IE外的各种主流浏览器中都实现了,可以放心使用。
- 专用线程只适合当前客户端使用,与其他客户端无关联,适用于本客户端内(本浏览器)的多线程使用。
共享线程
- 共享线程适用于多个客户端(多个浏览器)之间进行数据交互和控制,但是html5中没有类似锁机制,所以安全性存在一定问题。
- 共享线程可以有多个连接。用于解决多连接并发的问题。它们并不是绑定于一个HTML页面的。如果你在同一个浏览器上打开了同一个网站的页面,这些页面都可以访问其中任意页面创建的共享工作线程。
创建专用线程
- 创建一个工作线程需要使用Worker( )构造函数,把需要在线程中执行的JavaScript文件的文件名传给构造函数就可以了。
var worker = new Worker("myworker.js");
- 然后在实例上监听onmessage事件,来获取消息。
worker.onmessage = function(event){
//从工作线程获取消息
}
- 或者使用监听方法
worker.addEventListener("message",function(event){
//从工作线程获取消息
},false);
- 两种方式下,都可以在event对象的data属性中找到消息数据。数据已经自动地由JSON格式解码为原始格式,因此数据结构没有任何改变。
- 最后通过调用postMessage( )函数在不同线程中传递数据。
工作线程和它们的父线程通过一组公共的消息API进行通信。数据都是通过字符串的形式进行传递的,但是这并不意味着你只能发送字符串形式的而消息。如果你发送一些复杂的结构体,比如对象或者数组,它们将自动转换成JSON格式。但是DOM元素是不能转换成JSON的,因此其不能与DOM通信。
- 当你使用工作线程完成了既定的工作时,需要销毁线程。
在线程内部,使用close方法线程自己销毁自己。在线程外部的主线程中,使用线程实例的terminate方法销毁线程。
worker.terminate( );
- 使用其他脚本
工作线程可以使用全局方法importScripts来加载和使用其他的域内脚本文件或者类库。如:
importScripts(); importScripts('foo.js'); importScripts('foo.js', 'bar.js');
- 线程嵌套
在工作线程中还可以在创建子线程。
- 同步问题
Worker没有锁的机制,多线程的同步问题只能靠代码来解决(比如定义信号变量)。
共享线程与专用线程
共享线程可以有多个连接。用于解决多连接并发的问题。它们并不是绑定于一个HTML页面的。如果你在同一个浏览器上打开了同一个网站的页面,这些页面都可以访问其中任意页面创建的共享工作线程。
- 可以使用 SharedWorker()构造函数,创建共享工作线程。
除了脚本的路径外,这个构造函数还需要一个可选的name参数。如果name参数没有指定会使用一个空字符串。如果创建一个和既有实例使用相同脚本和名字的共享工作线程,只会为已存在的线程增加一个新的连接而并不会创建一个全新的线程。
- 共享线程不像专用工作线程那样拥有一个全局的message事件。他们必须来监听connect事件来获取新页面何时向共享工作线程创建创建连接。工作线程和创建连接那个线程之间的通信是基于触发message事件的port对象,利用的是其提供的postMessage( )。必须要调用port.start( )才能开始接收消息。
Web Worker 使用注意:
- 同源限制
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
- DOM 限制
Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。
- 通信联系
Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。
- 脚本限制
Worker 线程不能执行alert()方法和confirm()方法,但可以使XMLHttpRequest 对象发出 AJAX 请求。
- 文件限制
Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。
Web Worker的兼容性
webworker.png从图上看兼容性异常的好,甚至连IE系列都在好几年前就已经支持,但是这个兼容性只能说明能否使用Web Woker,这里的兼容并不能表明能在其中做其他操作。比如标准规定,可以在子线程做做计算、发起XHR请求等,但不能操作DOM对象,但实际使用中,Fecth在IE系列(包括Edge)浏览器中并不支持,会直接报错。