本文摘自PHP中文网,作者coldplay.xixi,侵删。
这是JavaScript栏目专门探索javascript及其所构建的组件的系列文章。
相关免费学习推荐:javascript(视频)
这次我们会逐步讲解 Web Workers,先说个简单的概念,接着讨论不同类型的 Web Workers,他们的组成部分是如何一起工作的,以及不同场景下它们各自优势和限制。最后,提供5个正确使用 Web Workers 的场景。
正如我们前面文章讨论的那样,你应该知道 JavaScript 语言采用的是单线程模型。然而,JavaScript 也为开发人员提供了编写异步代码的机会。
异步编程的局限性
以前的文章讨论过异步编程,以及应该在什么时候使用它。
异步编程可以让UI界面是响应式(渲染速度快)的,通过"代码调度",让需要请求时间的代码先放到在 event loop中晚一点再执行,这样就允许UI先行渲染展示。
异步编程的一个很好的用例就 AJAX 请求。由于请求可能花费大量时间,因此可以使用异步请求,在客户端等待响应的同时还可以执行其他代码。
然而,这带来了一个问题――请求是由浏览器的WEB API处理的,但是如何使其他代码是异步的呢?例如,如果成功回调中的代码非常占用CPU:
1 |
|
如果 performCPUIntensiveCalculation
不是一个HTTP请求而是一个阻塞代码(比如一个内容很多的for loop循环),就没有办法及时清空事件循环,浏览器的 UI 渲染就会被阻塞,页面无法及时响应给用户。
这意味着异步函数只能解决一小部分 JavaScript 语言单线程中的局限性问题。
在某些情况下,可以使用 setTimeout
对长时间运行的计算阻塞的,可以使用 setTimeout
暂时放入异步队列中,从让页面得到更快的渲染。例如,通过在单独的 setTimeout
调用中批处理复杂的计算,可以将它们放在事件循环中单独的“位置”上,这样可以争取为 UI 渲染/响应的执行时间。
看一个简单的函数,计算一个数字数组的平均值:
以下是重写上述代码并“模拟”异步性的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
使用setTimeout函数,该函数将在事件循环中进一步添加计算的每个步骤。在每次计算之间,将有足够的时间进行其他计算,从而可以让浏览器进行渲染。
Web Worker 可以解决这个问题
HTML5为我们带来了很多新的东西,包括:
- SSE(我们在前一篇文章中已经描述并与WebSockets进行了比较)
- Geolocation
- Application cache
- Local Storage
- Drag and Drop
- Web Workers
Web Worker 概述
Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。
你可能会问:“JavaScript不是一个单线程的语言吗?”
事实上 JavaScript 是一种不定义线程模型的语言。Web Workers 不是 JavaScript 的一部分,而是可以通过 JavaScript 访问的浏览器特性。历史上,大多数浏览器都是单线程的(当然,这已经改变了),大多数 JavaScript 实现都入发生在浏览器中。Web Workers 不是在 Node.JS 中实现的。Node.js 中有类似的集群(cluster)、子进程概念(child_process),他们也是多线程但是和 Web Workers 还是有区别 。
值得注意的是,规范 中提到了三种类型的 Web Workers:
- 专用 Workers (Dedicated Workers)
- 共享 Workers (Shared Workers)
- 服务 Workers (Service workers)
Dedicated Workers
专用 Workers 只能被创建它的页面访问,并且只能与它通信。以下是浏览器支持的情况:
Shared Workers
共享 Workers 在同一源(origin)下面的各种进程都可以访问它,包括:iframes、浏览器中的不同tab页(一个tab页就是一个单独的进程,所以Shared Workers可以用来实现 tab 页之间的交流)、以及其他的共享 Workers。以下是浏览器支持的情况:
Service workers
Service Worker 功能:
- 后台消息传递
- 网络代理,转发请求,伪造响应
- 离线缓存
- 消息推送
在目前阶段,Service Worker 的主要能力集中在网络代理和离线缓存上。具体的实现上,可以理解为 Service Worker 是一个能在网页关闭时仍然运行的 Web Worker。以下是浏览器支持的情况:
本文主要讨论 专用 Workers,没有特别声明的话,Web Workers、Workers都是指代的专用 Workers。
Web Workers 是如何工作
Web Workers 一般通过脚本为 .js
文件来构建,在页面中还通过了一些异步的 HTTP 请求,这些请求是完全被隐藏了的,你只需要调用 Web Worker API.
Worker 利用类线程间消息传递来实现并行性。它们保证界面的实时性、高性能和响应性呈现给用户。
Web Workers 在浏览器中的一个独立线程中运行。因此,它们执行的代码需要包含在一个单独的文件中。这一点很重要,请记住!
让我们看看基本 Workers 是如何创建的:
1 |
|
Worker()
构造函数的参数是一个脚本文件,该文件就是 Worker 线程所要执行的任务。由于 Worker 不能读取本地文件,所以这个脚本必须来自网络。如果下载没有成功(比如404错误),Worker 就会默默地失败。
为了启动创建的 Worker,需要调用 postMessage
方法:
1 |
|
Web Worker 通信
为了在 Web Worker 和创建它的页面之间进行通信,需要使用 postMessage
方法或 Broadcast Channel。
postMessage 方法
新浏览器支持JSON对象作为方法的第一个参数,而旧浏览器只支持字符串。
来看一个示例,通过将 JSON 对象作为一个更“复杂”的示例传递,创建 Worker 的页面如何与之通信。传递字符串跟传递对象的方式也是一样的。
让我们来看看下面的 HTML 页面(或者更准确地说是它的一部分):
相关阅读 >>
更多相关阅读请进入《javascript》频道 >>
Vue.js 设计与实现 基于Vue.js 3 深入解析Vue.js 设计细节
本书对 Vue.js 3 技术细节的分析非常可靠,对于需要深入理解 Vue.js 3 的用户会有很大的帮助。——尤雨溪,Vue.js作者