JavaScript多线程编程

浏览器端JavaScript是以单线程的方式执行的,也就是说JavaScript和UI渲染占用同一个主线程,那就意味着,若是JavaScript进行高负载的数据处理,UI渲染就颇有可能被阻断,浏览器就会出现卡顿,下降了用户体验。javascript

为此,JavaScript提供了异步操做,好比定时器(setTimeout、setInterval)事件、Ajax请求、I/O回调等。咱们能够把高负载的任务使用异步处理,它们将会被放入浏览器的事件任务队列(event loop)中去,等到JavaScript运行时执行线程空闲时候,事件队列才会按照先进先出的原则被一一执行。html

nodejs引觉得荣的异步处理

经过相似定时器,回调函数等异步编程方式在日常的工做中已经足够,可是若是作复杂运算,这种方式的不足就逐渐体现出来,好比settimeout拿到的值并不正确,或者页面有复杂运算的时候很容易触发假死状态,异步代码会影响主线程的代码执行,异步终究仍是单线程,不能从根本上解决问题。前端

多线程(Web Worker)就应运而生,它是HTML5标准的一部分,这一规范定义了一套 API,容许一段JavaScript程序运行在主线程以外的另一个线程中。将一些任务分配给后者运行。在主线程运行的同时,Worker(子)线程在后台运行,二者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(一般负责 UI 交互)就会很流畅,不会被阻塞或拖慢。java

1、什么是web worker

worker是window对象的一个方法,能够经过如下方式来检测你的浏览器是否支持workernode

if (window.Worker) {…… your code ……}
复制代码

一个worker是使用一个构造函数(Worker())建立的一个对象,这个构造函数须要传入一个的JavaScript文件,这个文件包含将在工做线程中运行的代码。相似于这样:git

let myWorker = new Worker('worker.js');
复制代码

worker经过postMessage() 方法和onmessage事件进行数据通讯。主线程和子线程是双向的,均可以发送和监听事件。向一个worker发送消息须要这样作(main.js):github

myWorker.postMessage('hello, world'); // 发送
worker.onmessage = function (event) { // 接收
	console.log('Received message ' + event.data);
	doSomething();
}
复制代码

postMessage所传的数据都是拷贝传递(ArrayBuffer类型除外),数据子线程也是相似传递(worker.js)web

addEventListener('message', function (e) {
	postMessage('You said: ' + e.data);
}, false);
复制代码

当子线程运行结束后,使用完毕,为了节省系统资源,能够手动关闭子线程。若是worker没有监听消息,那么当全部任务执行完毕(包括计数器)后,它就会自动关闭。ajax

// 在主线程中关闭
worker.terminate();
// 在子线程里线程
close();

// 监听 error 事件
worker.addEventListener('error', function (e) {
  console.log('ERROR', e);
});
复制代码

web worker自己很简单,可是它的限制特别多。chrome

2、使用的问题

一、同源限制
分配给Worker 线程运行的脚本文件(worker.js),必须与主线程的脚本文件(main.js)同源。这里的同源限制包括协议、域名和端口,不支持本地地址(file://)。这会带来一个问题,咱们常用CDN来存储js文件,主线程的worker.js的域名指的是html文件所在的域,经过new Worker(url)加载的url属于CDN的域,会带来跨域的问题,实际开发中咱们不会吧全部的代码都放在一个文件中让子线程加载,确定会选择模块化开发。经过工具或库把代码合并到一个文件中,而后把子线程的代码生成一个文件url。
解决方法:
(1)将动态生成的脚本转换成Blob对象。
(2)而后给这个Blob对象建立一个URL。
(3)最后将这个建立好的URL做为地址传给Worker的构造函数。

let script = 'console.log("hello world!");'
let workerBlob = new Blob([script], { type: "text/javascript" });
let url = URL.createObjectURL(workerBlob);
let worker = new Worker(url);
复制代码

二、访问限制
Worker子线程所在的全局对象,与主线程不在同一个上下文环境,没法读取主线程所在网页的 DOM 对象,也没法使用document、window、parent这些对象,global对象的指向有变动,window须要改写成self,不能执行alert()方法和confirm()等方法,只能读取部分navigator对象内的数据。另外chrome的console.log()却是可使用,也支持debugger断点,增长调试的便利性。
三、使用异步
Worker子线程中可使用XMLHttpRequest 对象发出 AJAX 请求,可使用setTimeout() setInterval()方法,也可以使用websocket进行持续连接。也能够经过importScripts(url)加载另外的脚本文件,可是仍然不能跨域。

3、应用场景

一、使用专用线程进行数学运算
Web Worke设计的初衷就是用来作计算耗时任务,大数据的处理,而这种计算放在worker中并不会中断前台用户的操做,避免代码卡顿带来没必要要的用户体验。例如处理ajax返回的大批量数据,读取用户上传文件,计算MD5,更改canvas的位图的过滤,分析视频和声频文件等。worker中除了缺失了DOM和BOM操做能力之外,仍是拥有很是强大的js逻辑运算处理的能力的,至关于nodejs一个级别的的运行环境。

二、高频的用户交互
高频的用户交互适用于根据用户的输入习惯、历史记录以及缓存等信息来协助用户完成输入的纠错、校订功能等相似场景,用户频繁输入的响应处理一样能够考虑放在web worker中执行。例如,咱们能够 作一个像Word同样的应用:当用户打字时后台在词典中进行查找,帮助用户自动纠错等等。

三、数据的预取
对于一些有大量数据的先后台交互产品,能够新开一个线程专门用来进行数据的预取和缓冲数据,本地web数据库的行写入和更改,长时间持续的运行,不会被主线程上的活动(好比用户点击按钮、提交表单)打断,也有利于随时响应主线程的通讯。也能够配合XMLHttpRequest和websocket进行不断开的通讯,实现守卫进程。

4、兼容性

整体来讲,兼容性仍是不错的, 移动端能够放心使用,桌面端要求不高的话,也可使用。

5、小结

对于web worker这项新技术,不管在PC仍是在移动web,腾讯新闻前端组进行了普遍的使用,Web Worker 的实现为前端程序带来了后台计算的能力,能够实现主 UI 线程与复杂计运算线程的分离,从而极大减轻了因计算量大而形成 UI 阻塞而出现的界面渲染卡、掉帧的状况,而且更大程度地利用了终端硬件的性能。superWorker能解决掉事件绑定,同源策略的问题,它目前最大的问题在于不兼容IE9,在兼容性要求不是那么严格的地方,尽量的使用吧!


《IVWEB 技术周刊》 震撼上线了,关注公众号:IVWEB社区,每周定时推送优质文章。

相关文章
相关标签/搜索