web worker就是一个后台执行JS文件的方法,能够给前端传递信息,前端也可以传递信息给web wokers.
if (window.Worker) {
// 是否可以使用workers ... }
index.js
if (window.Worker) {
var myWoker = new Worker('./work.js') myWoker.postMessage('send message'); myWoker.onmessage = function(e) {
console.log('Message received from worker',e.data); } }
work.js
onmessage = function(e) {
console.log('Message received from main script'); this.console.log(e.data) console.log('Posting message back to main script'); postMessage('I get'); }
输出情况
使用web workers还是比较简单的。
new Worker('path')
onmessage
e.data
postMessage
主动的发送信息给主线程。不想要workers怎么办?直接停掉它。terminate()
myWorker.terminate();
当然,如果workers不想干活了,也可以“罢工”
直接执行这个函数close();
即可
你当然可以 给workers添加错误回调,如监听数据接受的事件一样,只需要在worker设置好监听即可。
onerror = function(e) {
e.message// 错误事件 e.filename// 错误文件 e.lineno // 错误行号 }
onmessage = function(e) {
var a=function () {
postMessage('I get'+new Date()); setTimeout(a, 1000); } setTimeout(a, 1000); }
我们给主线程也添加异步事件。
var a1 = function () {
console.log('主线程'+new Date()); setTimeout(a1, 2000); } if (window.Worker) {
var myWoker = new Worker('./work.js') myWoker.postMessage('send message'); myWoker.onmessage = function(e) {
console.log('Message received from worker',e.data); } setTimeout(a1, 2000); }
符合多线程的表现,我们弄的复杂点,让主线程的执行超过1秒,看看是否会阻塞workers。
我们在主线程中添加了一个超过一秒的复杂运算,执行之后的情况如下。
首先说下workers,主线程的阻塞并不会影响workers的异步执行,只是会影响它的输出,因为它是通过发送消息给主线程输出的,所以会等主线程执行完,才会按照顺序执行workers 返回的事件队列。
对于主线程而言,阻塞已经影响到了异步了,因为每一次输出已经超过2秒
。
其实如果workers也有复杂的运算,表现也是和主线程一样的。
这里也可以看出来为什么workers不能操作DOM。
如果workers可以操作DOM的,那么很容易无法更新到最新的状态。
共享workers,页面之间需要符合同源策略。
index.js
if (!!window.SharedWorker) {
var myWorker = new SharedWorker("work.js"); myWorker.port.onmessage = function(e) {
console.log('Message received from worker'); } }
和workes的区别在于多一个端口调用。
必须设置一个onmessage
或者port.start()
什么时候使用start?
当我们需要addEventListener()
时候
如何使用?
父页面直接执行 myWorker.port.start()
workers执行 port.start()完成打开端口即可
这是为了与后续的work.js的connect
相呼应,因为相当于注册一个端口。
在workers是隐性的,并不是必需进行这步。
如果使用start打开端口,就需要在work.js中调用相同的方法。port.start()
我们来看看work.js的代码
var clients = []; onconnect = function(e) {
var port = e.ports[0]; clients.push(port); port.onmessage=function(e) {
clients.forEach((item,i)=>{
item.postMessage(e.data) }) } }
这里不在是onmessage
来设置监听事件了,我们使用一个变量,将所有的注册端口保存起来,这就是共享的意义,一个worker相当于一个单例,所以页面访问的是同一个上下文。
如果想要广播所有端口,就可以像例子中遍历端口发送数据。
如果你需要对某个特定的页面端口进行特别处理,你可以通过父页面的传递信息约定一个数据,进行判断处理事件。
注意:需要页面是同源的情况下才能访问。
Service worker是一个注册在指定源和路径下的事件驱动worker。
它采用JavaScript控制关联的页面或者网站,拦截并修改访问和资源请求,细粒度地缓存资源。
常见于网络不可用的情况下
·service worker
同样是workers的另一个类型。但是由于目前支持度不是很高,所以还是遇到很大的兼容性的问题,得不到广泛的使用。
HTTPS
承载如果一个网页没有了网络,那就将失去所有意义。
所以为了解决这个问题,就需要一个线程脚本,在没有网络连接的时候来控制网页。这样就导致离线页面和service worker的出现。有了这一功能,也代表着网页APP有了与原生APP叫板的底气。
生命周期:
用户首次访问service works控制的网站和网址的时候,service works会立刻下载。
之后每24小时就会被下载以此,期间可能频繁更新,不过每24小时一定会被下载一次,以避免不良脚本长时间生效。
如果这是首次启用service worker,页面会首先尝试安装,安装成功后它会被激活。
如果新版本的service worker已经下载完但是正在安装,但是不会被激活,这个时候称为 worker in waiting。
直到所有已经加载页面不再使用旧的service worker 的时候,才会激活新的service worker。新激活的service worker称为active worker
·'/sw-test/'·
,表示 app 的 origin 下的所有内容。如果你留空的话,默认值也是这个值, 我们在指定只是作为例子。
if ('serviceWorker' in navigator) {
// 读取js,进行注册 navigator.serviceWorker.register('/sw-test/sw.js', {
scope: '/sw-test/' }).then(function(reg) {
if(reg.installing) {
// 这里有判断service worker状态 console.log('Service worker installing'); } else if(reg.waiting) {
console.log('Service worker installed'); } else if(reg.active) {
console.log('Service worker active'); } }).catch(function(error) {
// registration failed console.log('Registration failed with ' + error); }); }
进行了上面的注册,就可以开始使用service worker的API了
// sw.js self.addEventListener('install', function(event) {
event.waitUntil( caches.open('v1').then(function(cache) {
return cache.addAll([ '/sw-test/', '/sw-test/index.html', '/sw-test/style.css', '/sw-test/app.js', '/sw-test/image-list.js', '/sw-test/star-wars-logo.jpg', '/sw-test/gallery/bountyHunters.jpg', '/sw-test/gallery/myLittleVader.jpg', '/sw-test/gallery/snowTroopers.jpg' ]); }) ); });
我们先讨论首次加载的情况
这里添加的是安装的监听事件。
waitUntil
是确保service worker安装前,读取到所有的数据。
caches.open()
方法,是创建一个缓存v1
区域,保存下面的文件。这里使用的是promise来实现。
service worker安装完成之后,就开始变成激活状态。
虽然都是缓存,但是service worker不允许使用localstorage
添加fetch
事件,进行操作。
self.addEventListener('fetch', function(event) {
event.respondWith(caches.match(event.request).then(function(response) {
// caches.match() always resolves // but in case of success response will have value if (response !== undefined) {
return response; } else {
return fetch(event.request).then(function (response) {
// response may be used only once // we need to save clone to put one copy in cache // and serve second one let responseClone = response.clone(); caches.open('v1').then(function (cache) {
cache.put(event.request, responseClone); }); return response; }).catch(function () {
return caches.match('/sw-test/gallery/myLittleVader.jpg'); }); } })); });
当service worker控制的资源被请求时候,就会触发fetch
事件。这些资源包含了指定的scope文档。
事件上的EVENT上的respondWith
劫持HTTPS的请求,劫持之后你就可以进行任何的操作
例子中caches.match
是用于匹配本地资源是否存在这个文件。
然后判断本地缓存是否有网络请求的响应报文,如果有则使用本地资源缓存,如果没有则使用fetch
进行请求,当然也可以使用xht
,如果使用后者,你还需要配置路径和方法,而fetch是封装好的API,只需要传入请求文段即可发送请求。
fetch
这个方法本身是支持promise
的,所以是异步进行请求的。
如果想要下次不需要网络请求,你可以将请求报文段保存到缓存,这样下次请求能够直接使用本地的缓存文件了。
最后如果不出意外,请求会返回一些数据,你可以将他们保存到本地,然后再返回给客户端。
cache.put(event.request, responseClone);
其实service worker更新一个中间代理服务器。它特别的地方是离线状态也可以访问,如果存在你需要的资源就可以返回给你,这样达到离线访问web app的目的。
当然,这种API自然对资源管理要求更加严格,如果滥用很可能会导致缓存过大,打开一个网页会让计算机占的内存变大等问题。目前支持度主要是谷歌的CHROM 和火狐的FIRFOX。这些都是默认关闭的,需要用户自己打开才能使用这个API,所以目前还处在测试的环节。
不过抛开这些短板来看,web app或许真的可能会代替native app。
其实上面的功能,普通的web workers都可以实现,但是由于service work已经封装好了API,如劫持HTTP请求的方法,对上面的场景实现起来非常方便。
MDN WEB WORKERS