之前咱们总说,JS是单线程没有多线程,当JS在页面中运行长耗时同步任务的时候就会致使页面假死影响用户体验,从而须要设置把任务放在任务队列中;执行任务队列中的任务也并不是多线程进行的,然而如今HTML5提供了咱们前端开发这样的能力 - Web Workers API,咱们一块儿来看一看 Web Worker 是什么,怎么去使用它,在实际生产中如何去用它来进行产出。javascript
Web Workers 使得一个Web应用程序能够在与主执行线程分离的后台线程中运行一个脚本操做。这样作的好处是能够在一个单独的线程中执行费时的处理任务,从而容许主(一般是UI)线程运行而不被阻塞。html
它的做用就是给JS创造多线程运行环境,容许主线程建立worker线程,分配任务给后者,主线程运行的同时worker线程也在运行,相互不干扰,在worker线程运行结束后把结果返回给主线程。这样作的好处是主线程能够把计算密集型或高延迟的任务交给worker线程执行,这样主线程就会变得轻松,不会被阻塞或拖慢。这并不意味着JS语言自己支持了多线程能力,而是浏览器做为宿主环境提供了JS一个多线程运行的环境。前端
不过由于worker一旦新建,就会一直运行,不会被主线程的活动打断,这样有利于随时响应主线程的通性,可是也会形成资源的浪费,因此不该过分使用,用完注意关闭。或者说:若是worker无实例引用,该worker空闲后当即会被关闭;若是worker实列引用不为0,该worker空闲也不会被关闭。java
看一看它的兼容性web
Browser | IE | Edge | FireFox | Chrome | Safari |
---|---|---|---|---|---|
version | 10+ | 12+ | 3.5+ | 4+ | 4+ |
worker线程的使用有一些注意点ajax
document
、window
等对象,可是能够获取navigator
、location(只读)
、XMLHttpRequest
、setTimeout族
等浏览器API。postMessage
方法来通讯。alert
、confirm
,但可使用 XMLHttpRequest
对象发出ajax请求。在主线程中生成 Worker 线程很容易:算法
var myWorker = new Worker(jsUrl, options)
Worker()构造函数,第一个参数是脚本的网址(必须遵照同源政策),该参数是必需的,且只能加载 JS 脚本,不然报错。第二个参数是配置对象,该对象可选。它的一个做用就是指定 Worker 的名称,用来区分多个 Worker 线程。canvas
// 主线程 var myWorker = new Worker('worker.js', { name : 'myWorker' }); // Worker 线程 self.name // myWorker
关于api什么的,直接上例子大概就能明白了,首先是worker线程的js文件:segmentfault
// workerThread1.js let i = 1 function simpleCount() { i++ self.postMessage(i) setTimeout(simpleCount, 1000) } simpleCount() self.onmessage = ev => { postMessage(ev.data + ' 呵呵~') }
在HTML文件中的body中:api
<!--主线程,HTML文件的body标签中--> <div> Worker 输出内容:<span id='app'></span> <input type='text' title='' id='msg'> <button onclick='sendMessage()'>发送</button> <button onclick='stopWorker()'>stop!</button> </div> <script type='text/javascript'> if (typeof(Worker) === 'undefined') // 使用Worker前检查一下浏览器是否支持 document.writeln(' Sorry! No Web Worker support.. ') else { window.w = new Worker('workerThread1.js') window.w.onmessage = ev => { document.getElementById('app').innerHTML = ev.data } window.w.onerror = err => { w.terminate() console.log(error.filename, error.lineno, error.message) // 发生错误的文件名、行号、错误内容 } function sendMessage() { const msg = document.getElementById('msg') window.w.postMessage(msg.value) } function stopWorker() { window.w.terminate() } } </script>
能够本身运行一下看看效果,上面用到了一些经常使用的api
主线程中的api,worker
表示是 Worker 的实例:
worker.postMessage
: 主线程往worker线程发消息,消息能够是任意类型数据,包括二进制数据worker.terminate
: 主线程关闭worker线程worker.onmessage
: 指定worker线程发消息时的回调,也能够经过worker.addEventListener('message',cb)
的方式worker.onerror
: 指定worker线程发生错误时的回调,也能够 worker.addEventListener('error',cb)
Worker线程中全局对象为 self
,表明子线程自身,这时 this
指向self
,其上有一些api:
self.postMessage
: worker线程往主线程发消息,消息能够是任意类型数据,包括二进制数据self.close
: worker线程关闭本身self.onmessage
: 指定主线程发worker线程消息时的回调,也能够self.addEventListener('message',cb)
self.onerror
: 指定worker线程发生错误时的回调,也能够 self.addEventListener('error',cb)
注意,w.postMessage(aMessage, transferList)
方法接受两个参数,aMessage
是能够传递任何类型数据的,包括对象,这种通讯是拷贝关系,便是传值而不是传址,Worker 对通讯内容的修改,不会影响到主线程。事实上,浏览器内部的运行机制是,先将通讯内容串行化,而后把串行化后的字符串发给 Worker,后者再将它还原。一个可选的 Transferable对象的数组,用于传递全部权。若是一个对象的全部权被转移,在发送它的上下文中将变为不可用(停止),而且只有在它被发送到的worker中可用。可转移对象是如ArrayBuffer,MessagePort或ImageBitmap的实例对象,transferList
数组中不可传入null。
更详细的API参见 MDN - WorkerGlobalScope。
worker线程中加载脚本的api:
importScripts('script1.js') // 加载单个脚本 importScripts('script1.js', 'script2.js') // 加载多个脚本
我的以为,Web Worker咱们能够当作计算器来用,须要用的时候掏出来摁一摁,不用的时候必定要收起来~
XMLHttpRequest
的。预加载图片
有时候一个页面有不少图片,或者有几个很大的图片的时候,若是业务限制不考虑懒加载,也可使用Web Worker来加载图片,能够参考一下这篇文章的探索,这里简单提要一下。
// 主线程 let w = new Worker("js/workers.js"); w.onmessage = function (event) { var img = document.createElement("img"); img.src = window.URL.createObjectURL(event.data); document.querySelector('#result').appendChild(img) } // worker线程 let arr = [...好多图片路径]; for (let i = 0, len = arr.length; i < len; i++) { let req = new XMLHttpRequest(); req.open('GET', arr[i], true); req.responseType = "blob"; req.setRequestHeader("client_type", "DESKTOP_WEB"); req.onreadystatechange = () => { if (req.readyState == 4) { postMessage(req.response); } } req.send(null); }
在实战的时候注意
在 Webpack 项目里面使用 Web Worker 请参照:怎么在 ES6+Webpack 下使用 Web Worker
至于还有Shared Worker、Service Worker什么的,咱们就不看了,IE不喜欢
网上的帖子大多深浅不一,甚至有些先后矛盾,在下的文章都是学习过程当中的总结,若是发现错误,欢迎留言指出~
参考:
PS:欢迎你们关注个人公众号【前端下午茶】,一块儿加油吧~
另外能够加入「前端下午茶交流群」微信群,长按识别下面二维码便可加我好友,备注加群,我拉你入群~