WebWorker类似于浏览器中的多线程操作。之前的JS中,无论你是使用setTimeout setIntever 还是 使用了XMLHttpRequest,都是在一个线程里面,前两个使用消息队列,XMLHttpRequest则是浏览器会帮你进行闲时进行,归根结底,都是在一个线程里面跑。如果用户想进行一些阻塞操作,很可能会产生卡住页面的情况。甚至于我们想实现一个类似于Android的专用 公用Service,那该怎么办?
H5新标准提出了WebWorker的概念,各个浏览器都各自实现了,先来看一下WebWorker能做什么。
WebWorker特点,在后台线程执行JS的能力,与页面通过send message这种方式通信。
WebWorker有两种,专用worker(dedicatedworker)与公用worker(sharedworker)。
疑问:worker与主线程如何同步,worker与主线程同时操作了一个DOM元素,会不会产生脏数据?所以,worker的能力被加以限制,不能访问DOM元素。
worker在chrome中是如何实现的?
chrome浏览器是多进程架构,分为Browser进程以及Render进程,每打开一个页面,浏览器都会为其分配一个Render进程,webkit以及js都运行在这个进程内。如果要起一个DedicatedWorker,Chrome会在Render进程中起一个线程。如果要起一个SharedWorker就稍微复杂一点,必须起一个专门的进程,并且,相同的SharedWorker不管你创建多少次,都只存在一个。
Android中的Chrome有一个限制,限定9个进程。
1.如果用户创建太多SharedWorker,可能第二个标签页都打不开?
2.SharedWorker的优先级如何定义?如果使用SharedWorker的页面都在后台,其优先级如何?
目前Android上的SharedWorker还处于讨论阶段,未实现。
下面看一下worker的基本用法,DedicatedWorker:
worker.js
1 2 3 4 5 |
this.addEventListener('message', function(e) { var data = e.data; console.log("worker: " + data); this.postMessage(data + 1); }); |
worker中需要一个this.onmessage接收消息
postMessage发送消息
参数在 event.data中
main.js
1 2 3 4 5 6 7 8 9 10 |
if (window.Worker) { var worker = new Worker("./worker.js"); worker.onmessage = function(e) { document.getElementById("worker").innerHTML = e.data; }; document.addEventListener('keydown', function(evt) { worker.postMessage(evt.keyCode); }); } |
使用Worker这个API来创建DedicatedWorker。
同样通过worker实例的onmessage和postMessage通信。
结束Worker:
在worker中,可以通过close()来kill掉自己
main中则调用worker.terminate()
如果worker运行中出现错误,在main中使用worker.onerror可以接收到错误消息
SharedWorker:
https://github.com/mdn/simple-shared-worker
SharedWorker跟DedicatedWorker有两个不同的地方:
1.通信不再直接通过worker,而是worker的port类
2.worker中需要实现onconnect,且其参数中有一个port列表,但目前只使用到了第一个
sharedworker.js
1 2 3 4 5 6 7 8 9 |
onconnect = function(e) { var port = e.ports[0]; port.onmessage = function(e) { var workerResult = 'Result: ' + (e.data[0] * e.data[1]); port.postMessage(workerResult); } } |
main.js
1 2 3 4 5 6 7 8 9 10 |
if (!!window.SharedWorker) { var myWorker = new SharedWorker("worker.js"); myWorker.port.postMessage([squareNumber.value,squareNumber.value]); myWorker.port.onmessage = function(e) { result2.textContent = e.data; console.log('Message received from worker'); } } |
当然 还可以写另一个页面,同样可以使用跟main.js类似的方法跟sharedworker通信。
ServiceWorker:
ServiceWorker是WebWorker的一种,它更加复杂,所以也更加强大。
首先,ServiceWorker有独立的生命周期:
1.Register:主线程调用API注册ServiceWorker
2.Installing:浏览器启动安装过程,加载和缓存一些静态资源
有可能失败进入Error状态
3.Activated:激活阶段,此阶段可以升级ServiceWorker
4.激活后,ServiceWorker会接管页面,如果页面是刚刚注册,本次不会被接管,下次加载页面才会接管。
5.ServiceWorker接管页面后,如果有fetch和message事件,会处于onfetch和onmessage,其他情况可能被终止。
ServiceWorker的特性:
1.它是一个worker,同样不能操作dom元素,同样可以通过postMessage与调用线程通信
2.ServiceWorker增加了网络处理,onfetch
3.ServiceWorker不被使用的时候,它会自己终止,再次使用会被重新激活,不要依靠它的内存来保存信息,请用webStorage或者indexDB。
4.ServiceWorker大量使用Promise,就是封装的一个callback标准
5.ServiceWorker权限很大,即所有网络请求都经过它,可以劫持连接,伪造和过滤响应,所以只能在https网页上注册ServiceWorker(是只能么?)。
6.ServiceWorker只作用于同域的fetch。onfetch有一个缓存的例子,我们通过缓存类缓存一些request和response,下次request,直接去缓存找response,处理失败了再去网络实时请求。注:response的类型需要是basic,即同域请求。
8.ServiceWorker的自动更新。
当网页激活时,浏览器会检查ServiceWorker是否有更新(有一个字节不同就会认为有更新),浏览器后台下载。
下载完开始运行,进入install状态,之后进入waitting状态。因为此时旧的ServiceWorker仍然在运行。
当页面被杀掉,旧去新来。
当然,如果你之前缓存了request,更新后需要清理一下。
现有问题:
如果在Install时失败了,页面无法感知。
ServiceWorker主要作用是在onfetch里面缓存/处理request。
https://github.com/GoogleChrome/samples
中有大量的onfetch与cache结合使用做离线应用的例子,在此不多赘述。
来自
http://blog.csdn.net/yl02520/article/details/14446763
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers