到 HTML5 总是让人津津乐道,太多的特性和有趣的 API 让人耳目一新。
但是很多童鞋还停留在语义化的阶段,忽视了 HTML5 的强劲之处。
一、明确 JavaScript 是单线程
JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。
听起来有些匪夷所思,为什么不设计成多线程提高效率呢?我们可以假设一种场景:
假定 JavaScript 同时有两个线程,一个线程在某个 DOM 节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
这决定了它只能是单线程,否则会带来很复杂的同步问题。
为了避免复杂性,从一诞生,JavaScript 就是单线程,这已经成了这门语言的核心特征,估计短期内很难改变。
二、新曙光:Web Worker
单线程始终是一个痛点,为了利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程。
但是子线程完全受主线程控制,且不得操作 DOM。
Web Workers 是现代浏览器提供的一个 JavaScript 多线程解决方案,我们可以找到很多使用场景:
1.我们可以用 Web Worker 做一些大计算量的操作;
2.可以实现轮询,改变某些状态;
3.页头消息状态更新,比如页头的消息个数通知;
4.高频用户交互,拼写检查,譬如:根据用户的输入习惯、历史记录以及缓存等信息来协助用户完成输入的纠错、校正功能等
5.加密:加密有时候会非常地耗时,特别是如果当你需要经常加密很多数据的时候(比如,发往服务器前加密数据)。
6.预取数据:为了优化网站或者网络应用及提升数据加载时间,你可以使用 Workers 来提前加载部分数据以备不时之需。
加密是一个使用 Web Worker 的绝佳场景,因为它并不需要访问 DOM 或者利用其它魔法,它只是纯粹使用算法进行计算而已。
随着大众对个人敏感数据的日益重视,信息安全和加密也成为重中之重。这可以从近期的 12306 用户数据泄露事件中体现出来。
三、兼容性
四、基本概念
1.首先记得去判断是否支持
2.创建一个新的 worker 很简单
3.postMessage 用来发送消息,而 onmessage 用来监听消息
在主线程中使用时,onmessage 和 postMessage() 必须挂在 worker 对象上,而在 worker中使用时不用这样做。
原因是,在 worker 内部,worker 是有效的全局作用域。
4.异常处理:
5.终止 worker
worker 线程会被立即杀死,不会有任何机会让它完成自己的操作或清理工作。
6.在 worker 线程中,workers 也可以调用自己的 close 方法进行关闭:
五、快速开始
为了快速掌握,我们来做一个小例子:项目结构如下
Html
main.js
Work.js
代码很简单,主线程发送:「写的真好!」
web worker 收到消息,发现内容中含有「好」字,回传给主线程:「谢谢支持」
六、局限性
1.在 worker 内,不能直接操作 DOM 节点,也不能使用 window 对象的默认方法和属性。
然而我们可以使用大量 window 对象之下的东西,包括 WebSockets,IndexedDB 以及 FireFox OS 专用的 Data Store API 等数据存储机制。
这里举个例子,我们修改 main.js:
再来修改 work.js
这时候运行就会报出:
这是因为:worker.js 执行的上下文,与主页面 HTML 执行时的上下文并不相同,最顶层的对象并不是 Window,woker.js 执行的全局上下文,而是 WorkerGlobalScope,下文我们具体说明。
2.workers 和主线程间的数据传递通过这样的消息机制进行:
双方都使用 postMessage() 方法发送各自的消息,使用 onmessage 事件处理函数来响应消息(消息被包含在 Message 事件的 data 属性中)。
3.同源限制
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
4.文件限制
Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自服务器。
5.不允许本地文件
Uncaught SecurityError: Failed to create a worker:
script at '(path)/worker.js'
cannot be accessed from origin 'null'.
那如何解决呢?我们可以启动一个本地服务器,建议使用 http-server,简单易用。
6.内容安全策略
因此普遍来说,worker 并不受限于创建它的document(或者父级 worker )的内容安全策略。
我们来举个例子,假设一个 document 有如下头部声明:
Content-Security-Policy: script-src 'self'
这个声明有一部分作用在于,禁止它内部包含的脚本代码使用 eval() 方法。然而,如果脚本代码创建了一个 worker,在 worker 上下文中执行的代码却是可以使用 eval() 的。
关于 CSP(Content security policy 内容安全策略),可以看我之前的这篇文章Web 安全之 CSP(Content Security Policy)。
有一个例外情况,即 worker 脚本的源如果是一个全局性的唯一的标识符(例如,它的 URL 指定了数据模式或者 blob),worker则会继承创建它的 document 或者 worker 的 CSP。
七、扩展:WorkerGlobalScope
关于 ,我们可以在 MDN 上面找到文档:
1.self:
我们可以使用 WorkerGlobalScope 的 self 属性来获取这个对象本身的引用。
2.location:
location 属性返回当线程被创建出来的时候与之关联的 WorkerLocation 对象,它表示用于初始化这个工作线程的脚步资源的绝对 URL,即使页面被多次重定向后,这个 URL 资源位置也不会改变。
3.close:
关闭当前线程,与 terminate 作用类似。
4.caches:
当前上下文得 CacheStorage,确保离线可用,同时可以自定义请求的响应。
5.console:
支持 console 语法。
6.importScripts
我们可以通过 importScripts() 方法通过 url 在 worker 中加载库函数。
7.XMLHttpRequest
有了它,才能发出 Ajax 请求。
8.可以使用:
还有很多 API 可以使用,这里就不一一举例了。
八、异常处理
当 worker 出现运行中错误时,它的 onerror 事件处理函数会被调用。它会收到一个扩展了 ErrorEvent 接口的名为 error 的事件。
该事件不会冒泡并且可以被取消。
错误事件我们常用如下这三个关键信息:
九、参考文章
1.JavaScript 运行机制详解:再谈Event Loop
2.MDN Web worker
3.从webWorker到serviceWorker
4.浅谈webWorker
十、热门原创文章
1.HTML5 之地理定位(Geolocation)
2.Git 版本控制之 Git-Flow
3.你了解软件测试吗?
eb worker是一个在后台运行的JavaScript程序,它不会影响当前页面的用户操作。 从事过Windows桌面编程的人,应该熟悉工作线程(Work Thread)的概念,该线程主要负责比较耗费时间的数据处理或通信方面的任务,当任务处理完成时,发送消息通知界面主线程(UI Thread)。 这样的多线程机制可以大大提高应用程序处理数据的能力,我们在前言中说过HTML5的设计目的之一就是想尽可能结合传统本地应用和网页之间的优点,Web Worker就是这样一个例子。文章最后给出了demo的访问网址。
和C++(版本>=11)以及Go这些系统级语言内置支持多线程并行计算不同,网页JavaScript脚本只支持单线程,如果在HTML页面中执行脚本,那么该页面将不能响应用户交互,直到脚本执行完成。 那么如果不幸该脚本在等待某个网络数据包或者正在执行一个大的循环处理,页面就将处于僵死状态。
Web Workers 是HTML5提供的一个JavaScript多线程解决方案。通过使用Web worker,执行任务交付给浏览器在后台默默运行,而不会影响当前页面的性能,用户可以继续任何其他操作,这将大大提升用户体验。
下面表格中的数字表示最早支持的版本号:
下面的例子演示了一个计数的网页工作线程:
Step 1: 检查浏览器支持
并非所有浏览器都支持该API,因此在创建Web worker之前,我们需要先检测该特性是否被用户浏览器所支持:
Step 2: 创建一个Web Worker文件
我们新建一个外部JavaScript文件,文件名为"demo_workers.js",代码如下(简单的计数):
上述代码中的postMessage方法很重要,依赖于该方法,工作线程才能和HTML页面(用户界面)之间通信,该方法发送一个消息回给页面。
Step 3:创建一个Web Worker对象
我们已经有了上述的web worker文件,现在我们需要从HTML页面调用它。下面这行代码检查worker是否已存在,如果不存在,则创建一个新的Web Worker对象,并运行"demo_workers.js"文件中的代码:
然后我们可以在Web worker中发送和接收消息。我们给该Web worker添加一个"onmessage"事件侦听器。
当web worker发回一个消息时,页面事件侦听器中的代码被执行。web worker发回的数据被保存在event.data中。
Step 4:终止Web Worker
当一个Web worker对象被创建后,它将持续侦听消息(即使外部脚本已经执行完成),除非被终止。要终止一个web worker,释放相关的浏览器/计算机资源,使用 terminate() 方法:
w.terminate();
Step 5:重用Web Worker
如果您在worker被终止后,将worker变量设置为未定义(undefined),就可以被重用:
w=undefined;
Web worker的代码在外部.js文件中,HTML页面的代码如下:
Demo源码可访问:http://www.ikinsoft.com/3ddemo/webWork.html
下是针对高级前端工程师的HTML相关面试题:
*请认真填写需求信息,我们会在24小时内与您取得联系。