前言

JS采用的是单线程机制,所有的任务只能在一个线程上完成,前面的任务没有完成就只能等待。随着计算机计算能力的增强,多核处理器的出现,单线程已不能充分的发挥计算机的算力,WebWorker就是给主线程创建多线程的环境,实现主线程运行时,WebWorker线程在后台运行,两者互不干扰,待WebWorker线程任务完成后,将结果返回给主线程。


一、WebWorker多线程机制

worker概念

1. worker发展历史

worker发展历史

worker在2009年就提出了草案,到2012年的时候,主流浏览器就已经全面的支持worker了。

worker规范

worker的规范有两个,分别是DedicatedWorker和SharedWorker。SharedWorker浏览器的兼容性问题较大,基本不使用,现在大家普遍所讲的worker都是遵从DedicatedWorker规范。

SharedWorker兼容性

从图中可以看到,SharedWorker的兼容性一路标红。
// 创建DedicatedWorker
const worker = new Worker("worker.js");
// 创建SharedWorker
var myWorker = new SharedWorker("worker.js");

2. 主线程和多线程

主线程

主线程

单线程中,JS阻塞用户交互和页面渲染

JS阻塞用户交互和页面渲染

多线程

多线程

3. 什么时候使用worker

1.减少卡顿可使用
	根据Chrome团队提出的用户感知性能模型RAIL,同步JS执行时间不能过长。量化来说,播放动画时建议小于16ms,用户操作响应建议小于100ms,页面打开到开始呈现内容建议小于1000ms。
2.worker只有在多核处理器上,才有可能性能提升
	进程是操作系统资源分配的基本单位,线程是操作系统调度CPU的基本单位。操作系统对线程能占用的CPU计算资源有复杂的分配策略。
	单核多线程通过时间切片交替执行。这是使用worker,可能会由于线程间的通信导致执行所花费时间的总时长更久。
	多核多进程可在不同核中真正并行。此时使用worker才能提高性能。

性能提升

通信消耗(所提升的性能 = 并行执行所节省的时间 - 通信消耗的时间)

通信开销

4. worker API

Worker API

// main.js
const worker = new Worker('./worker.js);
worker.postMessage('hello');
worker.onmessage = (event) => {
	console.log(event.data);	//world
}

// worker.js
self.onmessage = (event) => {
	console.log(event.data);	//hello
	poatMessage('world');
}

5. JS主线程和Worker线程的相同点和不同点

1.运行环境

运行环境

2.相同点
	1.包含完整的JS运行时(JS运行时有两个阶段:编译阶段和执行阶段),支持ECMAScript规范定义的语言语法和内置对象。

JS运行时的详细介绍:https://www.jianshu.com/p/672d512fdbae

	2.支持XmlHttpRequest,能独立发送网络请求与后台交互。
	3.包含只读的Location,指向Worker线程执行的script url,可通过url传递参数给Worker环境。
	4.包含只读的Navigator,用于获取浏览器信息,如通过Navigator.userAgent识别浏览器。
	5.支持setTimeout/setinterval计时器,可用于实现异步逻辑。
	6.支持WebSocket进行网络I/O;支持IndexedDB进行文件I/O。

3.不同点
	1.Worker线程没有DOM API,无法新建和操作DOM;也无法访问到主线程的DOM Element。
	2.Worker线程和主线程间内存独立,Worker线程无法访问页面上的全局变量(window,document等)和JavaScript函数。
	3.Worker线程不能调用alert()或confirm()等UI相关的BOM API。
	4.Worker线程被主线程控制,主线程可以新建和销毁Worker。
	5.Worker线程可以通过self.close自行销毁。

6. 浏览器的16ms渲染帧

日常使用屏幕的刷新频率是60HZ(也就是1秒钟更新60幅图像,1000/60 ~= 16ms)。

根据谷歌引擎为例分析结论:
	1. 一个渲染帧内commit的多次DOM改动会被合并渲染。
	2. 好事JS会造成丢帧。
	3. 渲染帧间隔为16ms左右。
	4. 避免耗时脚本、交错读写样式以保证流畅的渲染。

渲染帧流程:
	1. 脚本执行(JavaScript):脚本造成了需要重绘的改动,比如增删DOM、请求动画等。
	2. DOM树构建/样式计算(CSS Object Model):级联地生成每个节点的生效样式。
	3. 布局(Layout):计算布局,执行渲染算法。
	4. 重绘(Paint):各层分别进行绘制(比如3D动画)。
	5. 合成(Composite):合成各层的渲染结果。

执行队列
执行队列

优化

耗时超过16ms的JavaScipt可能会丢帧让页面变卡。如果有太多事情要做可以把这些工作重新设计,分割到各个阶段中执行。并充分利用缓存和懒初始化等策略。
不同执行时机的JavaScript有不同的优化方式。
	1. 服务器端渲染或者应用懒初始化策略。
	2. 耗时脚本可以优化算法或者迁移到Worker中。

总结

革命尚未成功,同志仍需努力。

更多推荐