本文引用至: web worker
因为浏览器的限制,注定了每一个网页只能在一个进程程当中运行, 并且,js又只能运行在一个线程当中. 因此, 做为一名开发者来讲, 对于这样的结果就只能呵呵了. 若是你想进行高复杂度的运算, 基本上就能够go die了(只要运行, 你网页基本上就崩掉了). 固然,聪明的W3C早就知道developer内心的小猫腻. 推出了web worker 这个概念. 咱们接下来,来正式接触一下 web worker吧.html
web worker 既然是一个线程. 那一定会设计到线程间的通讯. 这里,ww(web worker)提供了一个最简单的方法--postMessage(msg) 进行双向通讯.
来看一个简单的Demo:react
// index.html 中的 main.js var worker = new Worker('Worker.js'); worker.addEventListener('message', function(e) { console.log('Worker said: ', e.data); }, false); worker.postMessage('Hello World'); // Send data to our // Worker.js内容 self.addEventListener('message', function(e) { self.postMessage(e.data); }, false);
当载入main.js时, 在Console里,就会出现Hello World
的内容. 上面的例子实际上,已经说明了worker的工做原理. 在worker中,self就是 new Wroker的实例化的内容.
不过, 这里想强调一点, 经过postMessage传递的msg并非两个线程共享的.(要是共享的,不就GG了) 传递的Msg其实是一个副本, 最具备表明性的,应该就算是Object.jquery
// main.js 传递一个Object var worker = new Worker('Worker.js'); worker.addEventListener('message', function(e) { console.log('Worker said: ', msg.a); }, false); var msg = { a:2 } worker.postMessage(msg); // worker.js 接受,并返回 self.addEventListener('message', function(e) { e.data.a=3; self.postMessage(e.data); }, false); // 最后返回的结果是2
在一端向另一端传递msg时, 中间会通过serialized, 而后de-serialized 最终获得结果. 通俗一点就是:web
// 传递前 JSON.stringify(msg); // 解析数据 JSON.parse(msg);
当worker已经处理完毕,没有多大卵用以后. 就能够kill掉该线程.ajax
在web中, 提供了两种方法来关闭Web Worker. 关闭指定的Worker以后, 至关于即,kill 掉该线程. 因此, 这里须要注意一下:浏览器
worker.terminate(): 在外部终结该worker.并发
self.close(): 在worker内部自动终结.dom
官方推荐是,使用self.close进行内部的自动关闭. 这样能防止, 意外关闭正在运行的worker.函数
上面,在worker.js中,咱们使用self来获取worker自带的方法.post
self.addEventListener('message', function(e) { self.postMessage(e.data); }, false);
实际上, 在worker中, 他的全局索引就是self和this. 因此, 上面的代码能够简写为:
addEventListener('message', function(e) { postMessage(e.data); }, false);
worker 引用的就是js文件, 可能有些童鞋就会将worker当成通常js来使用. 可是,因为worker是独立的线程缘由,他和main js threading仍是有很大区别的.
他可以访问的权限有:
The navigator object: window.navigator 相关属性和方法
The location object (read-only): 只读的window.location内容.
XMLHttpRequest: 卧槽... 能够访问这个那就不得了了. worker就能够利用ajax来和后台进行通讯了.
setInterval()相关时间函数
剩下的就是不能访问的了。
web worker 中的error handler和window处理的方式,也是使用error时间进行监听.
worker.onerror = function(e){ throw new Error(e.message + " (" + e.filename + ":" + e.lineno + ")"); };
worker在访问时, 只能是在同一host下才行. 即, 你的worker只能处于指定目录下的path中。
// 这种状况下,就没法访问worker new Worker('http://crossdomain.com/worker.js');
另外, 若是你使用的是本地调试file://xxx
的话, 也不能使用worker.
在一个worker里面能够再spawn出其余的worker. 使用方法和在main.js中同样.
// 加载worker.js var sub_worker = new Worker('subworker.js');
subworker和worker有这一样的限制, 同域, 而且他的路由是相对于parent worker. 来看一个demo吧:
// main.js var worker = new Worker('worker.js'); worker.onmessage = function (event) { document.getElementById('result').textContent = event.data; }; // worker.js // 用来进行遍历计算 var num_workers = 10; var items_per_worker = 1000000; // start the workers var result = 0; var pending_workers = num_workers; for (var i = 0; i < num_workers; i += 1) { var worker = new Worker('subworker.js'); worker.postMessage(i * items_per_worker); worker.postMessage((i+1) * items_per_worker); worker.onmessage = storeResult; } // handle the results function storeResult(event) { result += 1*event.data; pending_workers -= 1; if (pending_workers <= 0) postMessage(result); // finished! } // subworker.js var start; onmessage = getStart; function getStart(event) { start = 1*event.data; onmessage = getEnd; } var end; function getEnd(event) { end = 1*event.data; onmessage = null; work(); } function work() { var result = 0; for (var i = start; i < end; i += 1) { // perform some complex calculation here result += 1; } postMessage(result); close(); }
另外,若是你想在当前的worker里面加载其余库文件, 就可使用importScripts
来导入.
// 导入其余库的文件 importScripts('jquery.js','react.js','react-dom.js');
根据worker 独立线程这一特性. 他的使用场景也很是清晰了.反正什么大规模数据并发,I/O操做的.均可以交给他来进行. 总的来讲有一下几种场景:
懒加载数据
文本分析
流媒体数据处理
web database的更新
大量JSON返回数据的处理
除了你们所熟知的web worker, 或者更确切的来讲--Dedicated workers.
总的来讲web worker分为两种:
Dedicated worker (DW): 即便用 new Worker()来建立的. 该worker通常只能和creator进行通讯. 即, 在建立worker的js script中才能使用.
Shared Wrokers (SW): 使用new SharedWorker() 进行建立. 他能在不一样的js script中使用.
具体来说SW和DW的区别就是一个只能在一个script中使用. 一个能够在不一样的script中使用.
看一个简单demo:
// index.html 发起shared worker 通讯 <script> var worker = new SharedWorker('sharedWorker.js'); worker.port.addEventListener("message", function(e) { console.log(e.data); }, false); worker.port.start(); // post a message to the shared web worker console.log("Calling the worker from script 1"); worker.port.postMessage("script-1"); </script> <script> console.log("Calling the worker from script 2"); worker.port.postMessage("script-2"); </script> // sharedWorker.js 内 var connections = 0; self.addEventListener("connect", function (e) { var port = e.ports[0]; connections++; port.addEventListener("message", function (e) { port.postMessage("Welcome to " + e.data + " (On port #" + connections + ")"); }, false); port.start(); }, false);
不过, SW的兼容性比较差, 能真正在实践场景使用的地方仍是少的. 因此,这里也只是当作了解.
SW 和 DW 同样, 也有一些features:
映入外部文件: importScripts()
错误监听: error事件监听
关闭通讯: port.close()
ajax交互: 有权访问xmlHttpRequest对象
能访问navigator object
访问 location object
setTimeout等时间函数