最近阅读《高性能JavaScript》时,第六章谈到“经过定时器将JavaScript执行代码的控制权先让给浏览器用于更新UI状态,而后再将控制权交回给JavaScript代码,这样就可使得页面更为流畅”,就联想到了以前理解的事件循环。html
这篇文章就是为了解释为何这么作能够提高页面的流畅度。html5
总所周知,JavaScript语言的一大特色就是单线程,也就是说在一个时间段里,JavaScript只能作一件事情(浏览器是多线程)。 多线程能够实现应用的并行处理,从而以更高的CPU利用率提升整个应用程序的性能和吞吐量。web
可是JavaScript却以单线程进行,为何呢?数据库
JavaScript是浏览器脚本语言,用于与用户交互以及操做DOM。 考虑以下状况,若是有两个并发的操做,对同一个DOM节点分别进行删除和修改样式,此时浏览器就没法决定到底采用哪一个线程的操做。相似数据库,咱们能够采用“锁”来处理并发,可是这会平添复杂度。因此,JavaScript语言没有支持多线程操做。 那又考虑这种状况,既然JavaScript是单线程,在某一时刻内只能执行特定的一个任务,而且会阻塞其它任务执行。那么若是用户触发了一个很是耗时的I/O操做,那么按道理后续的全部操做都得等到I/O操做完成后方可进行。可是,事实上,后续的任务没必要等待这个耗时的I/O操做完成,缘由就是JavaScript与生俱来的异步和回调。api
而这背后刚好就是本文的主题——————事件循环数组
事件循环包含了至少两个任务队列,宏任务队列和微任务队列。promise
宏任务包含建立文档对象、解析HTML、执行主线JavaScript代码、更改当前URL以及各类事件,例如页面加载、输入、网络事件和定时器等等。宏任务运行完成后,浏览器继续其余的任务调度,如从新渲染页面或者垃圾回收。浏览器
微任务包括promise、回调函数、DOM发生变化等。微任务更新应用程序的状态,必须在浏览器任务继续执行其余任务(渲染UI视图或者进行下一个宏任务)以前执行。网络
在微任务队列清空后,事件循环会检查当前是否须要从新渲染UI,若是须要则渲染UI视图。多线程
如今,用事件循环和简单的例子来分析《高性能的JavaScript》中的那句话。 需求:给包含1000个数字的数组中的每一个元素取绝对值(假设对一个数字进行需求操做耗时1ms)。
状况1(不使用定时器): 因为JavaScript主线程代码属于宏任务的一种,因此一次事件循环须要处理1000个数字,因此1s事件循环才进行到UI更新阶段,可是因为耗时过长,UI状态不会被更新,页面出现卡顿甚至堵塞。
状况2(使用定时器): 将一次处理1000个数字的任务分割为20个每次处理50个数字的任务。因为定时器是宏任务的一种,因此一次事件循环只处理50个数字,因为此时微任务队列为空,因此50ms后事件循环进行到UI更新阶段,而后根据状况进行UI渲染,页面未出现卡顿或者堵塞。
固然,若是只是单纯的处理数据,咱们能够考虑使用Web Workers。