本例子是经过经过红点展现地球上的地震带,数据来自于地质探测局经过console.log看到数据运算所耗的时间
不使用 webworker No web workers - all on main thread
使用一条 webworker One web worker
使用两条 Two web workers
使用八条 Eight web workers
使用20条 20 web workersjavascript
结论
:是? // 带着思考看下去html
JavaScript
引擎是单线程运行的,JavaScript
中耗时的I/O
操做都被处理为异步操做,它们包括键盘、鼠标I/O
输入输出事件、窗口大小的resize
事件、定时器(setTimeout、setInterval
)事件、Ajax
请求网络I/O
回调等。当这些异步任务发生的时候,它们将会被放入浏览器的事件任务队列中去,等到JavaScript
运行时执行线程空闲时候才会按照队列先进先出的原则被一一执行,但终究仍是单线程。html5
虽然JS
运行在浏览器中,是单线程的,每一个window
一个JS线程,但浏览器不是单线程的,例如Webkit
或是Gecko
引擎,均可能有以下线程:java
javascript引擎线程 界面渲染线程 浏览器事件触发线程 Http请求线程
不少人以为异步(promise async/await),都是经过相似event loop在日常的工做中已经足够,可是若是作复杂运算,这些异步伪线程的不足就逐渐体现出来,好比settimeout拿到的值并不正确,再者假如页面有复杂运算的时候页面很容易触发假死状态,
为了有多线程功能,webworker
问世了。不过,这并不意味着 JavaScript
语言自己就支持了多线程,对于 JavaScript
语言自己它还是运行在单线程上的, Web Worker
只是浏览器(宿主环境)提供的一个能力/API
。node
Web Worker
是HTML5标准的一部分,这一规范定义了一套 API
,它容许一段JavaScript
程序运行在主线程以外的另一个线程中。工做线程容许开发人员编写可以长时间运行而不被用户所中断的后台程序, 去执行事务或者逻辑,并同时保证页面对用户的及时响应,能够将一些大量计算的代码交给web worker运行而不冻结用户界面,后面会有案例介绍git
Web workers
可分为两种类型:专用线程dedicated web worker
,以及共享线程shared web worker
。 Dedicated web worker
随当前页面的关闭而结束;这意味着Dedicated web worker
只能被建立它的页面访问。与之相对应的Shared web worker
能够被多个页面访问。在Javascript
代码中,“Work”
类型表明Dedicated web worker
,而“SharedWorker”
类型表明Shared web worker
。
而Shared Worker
则能够被多个页面所共享(同域状况下)github
Web Worker的建立是在主线程当中经过传入文件的url来实现的。以下所示:web
let webworker = new Worker('myworker.js');
返回的是webworker
实例对象,该对象是主线程和其余线程的通信桥梁
主线程和其余线程能够经过ajax
onmessage: 监听事件 postmessage: 传送事件
相关的API进行通信
案例代码以下json
//主线程 main.js var worker = new Worker("worker.js"); worker.onmessage = function(event){ // 主线程收到子线程的消息 }; // 主线程向子线程发送消息 worker.postMessage({ type: "start", value: 12345 }); //web worker.js onmessage = function(event){ // 收到 }; postMessage({ type: "debug", message: "Starting processing..." });
若是在某个时机不想要 Worker
继续运行了,那么咱们须要终止掉这个线程,能够调用 在主线程worker
的 terminate
方法 或者在相应的线程中调用close
:
// 方式一 main.js 在主线程中止方式 var worker = new Worker('./worker.js'); ... worker.terminate(); // 方式2、worker.js self.close()
提供了onerror
API
worker.addEventListener('error', function (e) { console.log('MAIN: ', 'ERROR', e); console.log('filename:' + e.filename + '-message:' + e.message + '-lineno:' + e.lineno); }); // event.filename: 致使错误的 Worker 脚本的名称; // event.message: 错误的信息; // event.lineno: 出现错误的行号;
对于 Web Worker
,一个 tab
页面只能对应一个 Worker
线程,是相互独立的;
而 SharedWorker
提供了能力可以让不一样标签中页面共享的同一个 Worker
脚本线程;
固然,有个很重要的限制就是它们须要知足同源策略,也就是须要在同域下;
在页面(能够多个)中实例化 Worker
线程:
// main.js var myWorker = new SharedWorker("worker.js"); myWorker.port.start(); myWorker.port.postMessage("hello, I'm main"); myWorker.port.onmessage = function(e) { console.log('Message received from worker'); }
// worker.js onconnect = function(e) { var port = e.ports[0]; port.addEventListener('message', function(e) { var workerResult = 'Result: ' + (e.data[0]); port.postMessage(workerResult); }); port.start(); }
线程中再建立线程
在 Worker
线程的运行环境中没有 window
全局对象,也没法访问 DOM
对象,因此通常来讲他只能来执行纯 JavaScript
的计算操做。可是,他仍是能够获取到部分浏览器提供的 API
的:
setTimeout(), clearTimeout(), setInterval(), clearInterval():
有了设计个函数,就能够在 Worker
: 线程中能够再建立worker;XMLHttpRequest
: 对象:意味着咱们能够在 Worker 线程中执行 ajax
请求;navigator
对象:能够获取到 ppName,appVersion,platform,userAgent
等信息;location
对象(只读):能够获取到有关当前 URL
的信息;Application Cache
indexedDB
WebSocket、
Promise、
在线程中,提供了importScripts
方法
若是线程中使用了importScripts
通常按照如下步骤解析
一、解析 importScripts方法的每个参数。 二、若是有任何失败或者错误,抛出 SYNTAX_ERR 异常。 三、尝试从用户提供的 URL 资源位置处获取脚本资源。 四、对于 importScripts 方法的每个参数,按照用户的提供顺序,获取脚本资源后继续进行其它操做。
// worker.js importScripts('math_utilities.js'); onmessage = function (event) { var first=event.data.first; var second=event.data.second; calculate(first,second); // calculate 是math_utilities.js中的方法 };
也能够一次性引入多个
//能够多起一次传入 importScripts('script1.js', 'script2.js');
onmessage = function(evt){ var xhr = new XMLHttpRequest(); xhr.open("GET", "serviceUrl"); //serviceUrl为后端j返回son数据的接口 xhr.onload = function(){ postMessage(xhr.responseText); }; xhr.send(); }
// 设置jsonp function MakeServerRequest() { importScripts("http://SomeServer.com?jsonp=HandleRequest"); } // jsonp回调 function HandleRequest(objJSON) { postMessage("Data returned from the server...FirstName: " + objJSON.FirstName + " LastName: " + objJSON.LastName); } // Trigger the server request for the JSONP data MakeServerRequest();
从一个线程到另外一个线程的通信其实是一个值拷贝的过程,其实是先将数据JSON.stringify
以后再JSON.parse
。主线程与子线程之间也能够交换二进制数据,好比File、Blob、ArrayBuffer
等对象,也能够在线程之间发送。可是,用拷贝方式发送二进制数据,会形成性能问题。好比,主线程向子线程发送一个50MB文件,默认状况下浏览器会生成一个原文件的拷贝。为了解决这个问题,JavaScript容许主线程把二进制数据直接转移给子线程,转移后主线程没法再使用这些数据,这是为了防止出现多个线程同时修改数据的问题,这种转移数据的方法,叫作Transferable Objects。
不过如今不少浏览器支持transferable objects(可转让对象)
,这个技术是零拷贝转移,能大大提高性能,
能够指定传送的数据全都是零拷贝
var abBuffer = new ArrayBuffer(32); aDedicatedWorker.postMessage(abBuffer, [abBuffer]);
也能够 指定某个是 使用 零拷贝
var objData = { "employeeId": 103, "name": "Sam Smith", "dateHired": new Date(2006, 11, 15), "abBuffer": new ArrayBuffer(32) }; aDedicatedWorker.postMessage(objData, [objData.abBuffer]);
工做线程之间的通讯必须依赖于浏览器的上下文环境,而且经过它们的 MessagePort
对象实例传递消息。每一个工做线程的全局做用域都拥有这些线程的端口列表,这些列表包括了全部线程使用到的 MessagePort
对象。在专用线程的状况下,这个列表还会包含隐式的 MessagePort
对象。
每一个工做线程的全局做用域对象 WorkerGlobalScope
还会有一个工做线程的线程列表,在初始化时这个列表为空。当工做线程被建立的时候或者拥有父工做线程的时候,它们就会被填充进来。
最后,每一个工做线程的全局做用域对象 WorkerGlobalScope
还拥有这个线程的文档模型,在初始化时这个列表为空。当工做线程被建立的时候,文档对象就会被填充进来。不管什么时候当一个文档对象被丢弃的时候,它就要从这个文档对象列举里面删除出来。
// 部分机器webwoker初始化时间 Macbook Pro: 2 workers, 0.4 milliseconds on average Macbook Pro: 4 workers, 0.6 milliseconds on average Nexus 5: 2 workers, 6 milliseconds on average Nexus 5: 4 workers, 15 milliseconds on average (border-line UI jank)
一、普通json/object
二、tranferable objects
可见 transferable objects
传输速度要高不少
1) 使用专用线程进行数学运算Web Worker
最简单的应用就是用来作后台计算,而这种计算并不会中断前台用户的操做
2) 图像处理
经过使用从<canvas>
或者<video>
元素中获取的数据,能够把图像分割成几个不一样的区域而且把它们推送给并行的不一样Workers
来作计算
3) 大量数据的检索
当须要在调用 ajax
后处理大量的数据,若是处理这些数据所需的时间长短很是重要,能够在Web Worker
中来作这些,避免冻结UI线程
。
4) 背景数据分析
因为在使用Web Worker
的时候,咱们有更多潜在的CPU
可用时间,咱们如今能够考虑一下JavaScript
中的新应用场景。例如,咱们能够想像在不影响UI体验的状况下实时处理用户输入。利用这样一种可能,咱们能够想像一个像Word(Office Web Apps 套装)
同样的应用:当用户打字时后台在词典中进行查找,帮助用户自动纠错等等。
一、不能访问DOM和BOM
对象的,Location和navigator
的只读访问,而且navigator
封装成了WorkerNavigator
对象,更改部分属性。没法读取本地文件系统
二、子线程和父级线程的通信是经过值拷贝,子线程对通讯内容的修改,不会影响到主线程。在通信过程当中值过大也会影响到性能(解决这个问题能够用transferable objects
)
三、并不是真的多线程,多线程是由于浏览器的功能
四、兼容性
5 由于线程是经过importScripts
引入外部的js
,而且直接执行,实际上是不安全的,很容易被外部注入一些恶意代码
六、条数限制,大多浏览器能建立webworker线程的条数是有限制的,虽然能够手动去拓展,可是若是不设置的话,基本上都在20条之内,每条线程大概5M左右,须要手动关掉一些不用的线程才可以建立新的线程(相关解决方案)
七、js存在真的线程的东西,好比SharedArrayBuffer
一、tagg2
参考文献:[1] https://www.html5rocks.com/zh...
[2] http://www.alloyteam.com/2015...
[3] https://typedarray.org/concur...
[4] http://www.andygup.net/advanc...
[5] https://developer.mozilla.org...
[6] http://coolaj86.github.io/htm...
[7] http://www.xyhtml5.com/webwor...