当前位置: 首页 > Web前端 > HTML5

WebWorker初探

时间:2023-04-04 23:56:46 HTML5

以前我们总说JS是单线程的,不是多线程的。当JS在页面运行长时间耗时的同步任务时,会导致页面卡顿,影响用户体验。所以需要设置任务放入任务队列。中等的;执行任务队列中的任务不是多线程的,但是现在HTML5为我们提供了前端开发的能力——WebWorkersAPI,我们来看看WebWorker是什么,怎么用,怎么用它在实际生产中如何使用它进行输出。1.概述WebWorkers使Web应用程序能够在与主执行线程分开的后台线程中运行脚本操作。这样做的好处是可以在单独的线程中执行耗时的处理任务,让主(通常是UI)线程运行而不会被阻塞。它的作用是为JS创建一个多线程的运行环境,让主线程创建一个工作线程,并将任务分配给后者。主线程运行的同时,工作线程也在运行,互不干扰。工作线程运行结束后,将结果返回给主线程。这样做的好处是主线程可以将计算量大或延迟高的任务交给工作线程执行,这样主线程就会变得轻松,不会被阻塞或变慢。这并不是说JS语言本身就支持多线程能力,而是浏览器作为宿主环境为JS提供了多线程的运行环境。但是,worker一旦创建,就会一直运行,不会被主线程的活动打断。这样有利于随时响应主线程的连接,但也会造成资源的浪费,所以不宜过度使用,用完了注意关闭。.也就是说:如果worker没有实例引用,则worker空闲后会立即关闭;如果workerrealcolumnreference不为0,则worker空闲时不会关闭。看看它的兼容性BrowserIEEdgeFireFoxChromeSafariversion10+12+3.5+4+4+2。使用2.1限制worker线程的使用有几点需要注意当然不能让worker线程在别人电脑上到处读取文件。文件限制为了安全起见,工作线程不能读取本地文件。它加载的脚本必须来自网络,并且需要和主线程的脚本是同一个源DOM。该操作限制工作线程在不同于主线程窗口的另一个全局上下文中运行。它不能读取主线程所在网页的DOM对象,也不能获取document、window等对象,但可以获取navigator、location(只读)、XMLHttpRequest、setTimeout家族等浏览器API。通信限制工作线程和主线程不在同一个上下文中,不能直接通信。他们需要通过postMessage方法进行通信。该脚本限制工作线程执行警报和确认,但它可以使用XMLHttpRequest对象发送ajax请求。2.2例子在主线程中生成一个Worker线程很简单:varmyWorker=newWorker(jsUrl,options)Worker()构造函数,第一个参数是脚本的URL(必须遵守同源策略),这个参数是必须的,而且只能加载JS脚本,否则会报错。第二个参数是一个配置对象,它是可选的。它的作用之一是指定Worker的名称,以区分多个Worker线程。//MainthreadvarmyWorker=newWorker('worker.js',{name:'myWorker'});//Workerthreadself.name//MyWorker是关于api的,直接看例子大概就明白了,先ofall是工作线程的js文件://workerThread1.jsleti=1functionsimpleCount(){i++self.postMessage(i)setTimeout(simpleCount,1000)}simpleCount()self.onmessage=ev=>{postMessage(ev.data+'呵呵~')}HTML文件正文中:

Worker输出内容:发送停止!
可以自己跑一下看看效果。上面使用了api主线程中一些常用的api,worker表示是Worker的一个实例:worker.postMessage:主线程向工作线程发送消息。消息可以是任何类型的数据,包括二进制数据。worker.terminate:主线程关闭工作线程。worker.onmessage:指定工作线程发送消息时的回调。也可以使用worker.addEventListener('message',cb)worker.onerror:来指定worker线程出错时的回调,或者worker.addEventListener('error',cb)Worker线程中的全局对象是self,代表子线程本身,此时this指向self,上面有一些API:self.postMessage:工作线程向主线程发送消息,消息可以是任意类型的数据,包括二进制数据self.close:工作线程关闭自己self.onmessage:指定主线程发送工作线程消息时的回调,也可以是self.addEventListener('message',cb)self.onerror:指定当回调工作线程出错,或者self.addEventListener('error',cb)注意,w.postMessage(aMessage,transferList)方法接受两个参数。aMessage可以传输任何类型的数据,包括对象。这种通信是一种复制关系,即传递值而不是地址。WOrker对通信内容的修改不会影响主线程。实际上,浏览器内部运行机制是先将通信内容序列化,然后将序列化后的字符串发送给Worker,Worker再发送reduce。一个可选的Transferable对象数组,用于转移所有权。如果一个对象的所有权被转移,它在它被发送到的上下文中变得不可用(中止),并且只在它被发送到的工作人员中可用。transferable对象为ArrayBuffer、MessagePort或ImageBitmap等实例对象,transferList数组中不能传入null。有关更详细的API,请参阅MDN-WorkerGlobalScope。在工作线程中加载脚本的API:importScripts('script1.js')//加载单个脚本importScripts('script1.js','script2.js')//加载多个脚本3.个人而言,WebWorker我们可以当计算器用,需要的时候拿出来按一下,不需要的时候收起来~有些加密数据的加解密算法比较复杂,或者加解密很多数据的时候,会非常耗费计算资源,导致UI线程无响应,所以这是使用WebWorker的好时机,它可以让用户更无缝地操作UI。预取数据有时为了提高数据加载速度,可以使用Worker线程提前获取数据,因为Worker线程可以使用XMLHttpRequest。预渲染在一些渲染场景中,比如渲染一个复杂的画布,需要计算反射、折射、光影、材质等效果。这些计算的逻辑可以使用Worker线程来执行,也可以使用多个Worker线程。这里有一个光线追踪的例子。在复杂的数据处理场景中,某些检索、排序、过滤、分析是非常耗时的。在这种情况下,可以使用WebWorker来完成,而不占用主线程。预加载图片有时候一个页面有很多图片,或者有几张大图,如果业务限制不考虑懒加载,也可以使用WebWorker来加载图片。可以参考这篇文章的探索,这里简单总结一下。//主线程letw=newWorker("js/workers.js");w.onmessage=function(event){varimg=document.createElement("img");img.src=window.URL.createObjectURL(event.data);document.querySelector('#result').appendChild(img)}//工作线程letarr=[...许多图像路径];for(leti=0,len=arr.length;i{if(req.readyState==4){postMessage(req.response);}}req.send(null);}注意,虽然使用worker线程不会占用主线程,但是启动worker会消耗资源。在主线程中使用XMLHttpRequest。在请求过程中,浏览器又开启了一个异步的http请求线程,但是在交互过程中仍然消耗了主线程资源。在Webpack项目中使用WebWorker可以参考:如何在ES6+Webpack下使用WebWorker至于SharedWorker、ServiceWorker等就不看了。IE不喜欢网上的帖子大多深浅不一,甚至有些不一致。以下文章都是学习过程中的总结,如果发现错误,欢迎留言指出~参考:MDN-WebWorker概念与使用阮一峰-WebWorker教程JavaScript工作原理7-WebWorkers项目中WebWorker的分类及5个使用场景ES6+Webpack下WebWorkerPS的使用方法:欢迎大家关注我的公众号【前端下午茶】,一起努力吧~另外可以加入“前端下午茶交流群”微信群,长按识别下面二维码可以加我为好友,加群备注,我拉你进群~