Web Worker 介绍

众所周知,JavaScript 这门语言的一大特点就是单线程,即同一时间只能同步处理一件事情,这也是这门语言衍生出的 nodeJS 被各后端大佬诟病的很重要的一点。

然而,JavaScript 在设计之初,其实是故意被设计成单线程语言的,这是由于它当时的主要用途决定的。

JavaScript 最初的设计初衷是完成页面与用户的交互,操作 DOM 或者 BOM 元素,此时如果一味地追求效率使用多线程的话,会带来资源抢占,数据同步等等问题,因此必须规定,同一时间只有一个线程能直接操作页面元素,以保证系统的稳定性以及安全性。

尽管如此,但是 JavaScript 并不是只能线性处理任务。JS 拥有消息队列和事件循环机制,通过异步处理消息的能力来实现并发。在高 I/O 型并发事务处理的过程中,由于不需要手动生成与销毁线程以及占用额外管理线程的空间,性能表现及为优异。因此,nodeJS 作为 JavaScript 在服务端的探索者,在处理高并发网络请求的优势极为明显。

尽管 JavaScript 通过异步机制完美解决了高 I/O 性能的问题,但 JavaScript 单线程执行的本质还是没有变的。因此缺点就显而易见了,那就是处理 CPU 密集型的事务时没有办法充分调动现代多核心多线程机器的运算资源。

在现代大型前端项目中,随着代码的复杂程度越来越高,本地的计算型事务也在变得繁重,而运行在单线程下 JS 项目必定会忙于处理计算而无暇顾及用户接下来的频繁操作,造成卡顿等不太好的用户体验,更严重的情况是,当计算型事务过多时还有可能因为资源被占满带来网页无响应的卡死现象。因此,Web 项目的本地多线程运算能力势在必行,由此,Web Worker 应运而生了。

Web Worker 是 HTML5 中推出的标准,官方是这样定义它的:

Web Workers makes it possible to run a script operation in a background thread separate from the main execution thread of a web application.

它允许 JavaScript 脚本创建多个线程,从而充分利用 CPU 的多核计算能力,不会阻塞主线程 (一般指 UI 渲染线程) 的运行。

Web Worker 虽然是 HTML5 标准,但其实早在 2009 年 W3C 就已经提出了草案,因此它的兼容性良好,基本覆盖了所有主流浏览器。

20200703113954

Web Worker 的局限

需要注意的是,Web Worker 本质上并没有突破 JavaScript 的单线程的性质。

事实上,Web Worker 脚本中的代码并不能直接操作 DOM 节点,并且不能使用绝大多数 BOM API。它的全局环境是 DedicatedWorkerGlobalScope 而并不是 Window。运行 Worker 的实际上是一个沙箱,跑的是与主线程完全独立 JavaScript 文件。

Worker 做的这些限制,实际上也是为了避免文章开头说过的抢占问题。它更多的使用场景是作为主线程的附属,完成高 CPU 计算型的数据处理,再通过线程间通信将执行结果传回给主线程。在整个过程中,主线程仍然能正常地相应用户操作,从而很好地避免页面的卡顿现象。

Web Worker 的使用

新建

目前 Web Worker 的浏览器支持已经较为完善,基本上直接传入 Worker 脚本的 URI 并实例化即可使用。

通信

Worker 与主线程之间的通信只需要各有两个 API:onmessage/addEventListener 与 postMessage 即可完成收发消息的交互。

注:Worker 中,this.xx, self.xx 与直接使用 xx,其作用域都指向 worker 的全局变量 DedicatedWorkerGlobalScope ,可以互换。

销毁

Worker 的销毁方式有两种,既能在内部主动销毁,也能够被主线程通知销毁。

进阶:让通信方式 Promise 化

根据上一节,我们已经能够简单地使用 Worker 的 API 来获取浏览器多线程计算的能力,但是它离工程化的应用还缺少了一些易用性,比如我们多数时候需要使用到的异步相应。接下来我们就来做这件事情。

首先我们需要一个异步回调集合 actionHandlerMap,用于存放等待 Worker 响应的 Promise resolve 方法,其 key 值可以用通信中的某一 id 指定(保证其唯一性即可)。接着我们需要封装一下原生的 postMessage 与 onmessage 方法。

我们在原生的 postMessage 发送的信息中加入 id,并将当前的 Promise 的 resolve 方法放入 actionHandlerMap,等待 Worker 返回结果后触发。

对于 onmessage 的监听,在接收到 Worker 发送过来的响应之后,匹配响应的 Promise 并执行 .then() 方法,完成后删除集合中的 Promise resolve 函数。

对于 worker 部分的处理就简单得多,计算处理完毕后,在响应回复中带上请求的 id 即可。

当然,worker 这边的改造还能够更进一步。我们发现,当 Worker 需要接收的计算种类增多,使用 switch 方式包裹的 onmessage 函数就会变得冗长,使用字符串判断也不够可靠,我们可以用策略模式简单地封装一下 Worker 中的逻辑。

至此,一个简单好用的 Promise Worker 就建立完成了。

当然,为了增加框架的鲁棒性,我们还应该加入类似于错误处理,报错及监控数据上报等等能力。由于不属于本文探讨的范围,这里就先按住不表,有兴趣的读者可以参看 AlloyTeam 最新开源的 alloy-worker 项目,其中对上述存在的问题进行了全面的补足,是一个较为完善的高可用的 Worker 通信框架。

总结

本文对 Web Worker 进行了简要的介绍,包括其能力以及局限性,让读者对 Worker 的使用场景有一个全面的了解。提出了一种封装 Worker 原生 API 使之能被 Promise 化调用的解决方案,并在最后推荐了团队内正在使用的功能完善的成熟解决方案,希望能帮助到近期有兴趣进行 Worker 改造的前端开发者们。

脚本错误量极致优化-定位压缩且无 SourceMap 文件的脚本错误

”JS 代码压缩后,定位具体出错代码困难!“ 的问题,我们可以通过 SourceMap 快速定位,处理得到源文件的具体错误信息。具体方式可以查看 《脚本错误量极致优化-让脚本错误一目了然》

然而当项目外链第三方资源或公共库时,这种压缩且无提供 SourceMap 的文件出现异常,又该如何更好的定位错误位置呢?

背景

需求预沟通

  • 产品:我们需要做一个 IM,和微信一样
  • 我:打扰了,走错片场了,你们继续,,,,,,,
  • ....
  • 我:这个需求虽然不合理,但是我还是接了。。。。。。

TypeScript 中高级应用与最佳实践

当我们讨论 TypeScript 时,我们在讨论什么?

TAT.yaoyao 搞懂闭包
In JavaScript on 2019年07月20日 by view: 1,880
4

闭包这个概念是前端工程师必须要深刻理解的,但是网上确实有一些文章会让初学者觉得晦涩难懂,而且闭包的文章描述不一。

本文面向初级的程序员,聊一聊我对闭包的理解。当然如果你看到闭包联想不到作用域链垃圾回收也不妨看一眼。希望读了它之后你不再对闭包蒙圈。

chuangchuang React 组件开发实践
In JavaScript on 2019年07月20日 by view: 1,374
0

基于 React 的组件化开发方式,为富前端 web 应用提供大量技术实践,社区逐渐形成了稳定的组件规范,本文从 API 层面归纳出 6 种组件类型,分析其优缺点和适用场景,为日常组件开发提供一个方法指南。6 种类型分别为结构型组件、样式型组件、组合型组件、配置型组件、受控型组件、非受控组件。

写在前面

Omi 框架 正式发布了 → omi-transform

Made css3 transform super easy. Made 60 FPS easy.

作为 Omi 组件化开发特效运动解决方案,让你轻松在 Omi 项目里快速简便支持 CSS3 Transform 设置。css3transform 是经受过海量项目洗礼的,作为移动 Web 特效解决方案,在微信、手 Q 兴趣部落、日迹、QQ 群、QQ 附近等项目中广泛使用,以激进的修改 DOM 属性为代价,带来极为便利的可编程性。

你可以通过 css3transform 官方首页快速了解它。

AlloyTeam ESLint 配置指南
In JavaScript on 2017年08月28日 by view: 17,410
10

ESLint 是一个应用广泛的 JavaScript 代码检查工具。本文主要介绍由 AlloyTeam 总结的 ESLint 配置,以及应用 ESLint 配置时的一些经验和工具。

配置原则

我们依据以下三条原则,研读了 ESLint 所有的配置项,定制出了心目中「完美」的 ESLint 配置

  1. 能够帮助发现代码错误的规则,全部开启
  2. 配置不应该依赖于某个具体项目,而应尽可能的合理
  3. 帮助保持团队的代码风格统一,而不是限制开发体验
TAT.dorsywang 剖析 Babel——Babel 总览
In JavaScript,Web开发 on 2017年04月06日 by view: 12,926
1

名词解释

AST:Abstract Syntax Tree, 抽象语法树

DI: Dependency Injection, 依赖注入

===============================================================

Babel 的解析引擎

Babel 使用的引擎是 babylon,babylon 并非由 babel 团队自己开发的,而是 fork 的 acorn 项目,acorn 的项目本人在很早之前在兴趣部落 1.0 在构建中使用,为了是做一些代码的转换,是很不错的一款引擎,不过 acorn 引擎只提供基本的解析 ast 的能力,遍历还需要配套的 acorn-travesal, 替换节点需要使用 acorn-,而这些开发,在 Babel 的插件体系开发下,变得一体化了

Omi

Open and modern framework for building user interfaces.


  • Omi 的 Github 地址 https://github.com/AlloyTeam/omi
  • 如果想体验一下 Omi 框架,可以访问 Omi Playground
  • 如果想使用 Omi 框架或者开发完善 Omi 框架,可以访问 Omi 使用文档
  • 如果你想获得更佳的阅读体验,可以访问 Docs Website
  • 如果你懒得搭建项目脚手架,可以试试 omi-cli
  • 如果你有 Omi 相关的问题可以 New issue
  • 如果想更加方便的交流关于 Omi 的一切可以加入 QQ 的 Omi 交流群 (256426170)