对于event loop 能够抽象成一段简单的代码表示javascript
for (macroTask of macroTaskQueue) { // 1. Handle current MACRO-TASK handleMacroTask(); // 2. Handle all MICRO-TASK for (microTask of microTaskQueue) { handleMicroTask(microTask); } }
javascript是一个单线程语言,同一时间只能执行一个任务。
对于javascript的事件处理机制,咱们能够简单理解成“主线程+任务队列”模式。主要步骤以下html
(1)全部同步任务都在主线程上执行,造成一个执行栈。(2)主线程以外,还存在一个 "任务队列"(task queue)。只要异步任务有了运行结果,就在 "任务队列" 之中放置一个事件。
(3)一旦 "执行栈" 中的全部同步任务执行完毕,系统就会读取 "任务队列",看看里面有哪些事件。那些对应的异步任务,因而结束等待状态,进入执行栈,开始执行。java(4)主线程不断重复上面的第三步。web
任务队列分为task queue和microtask queue。执行栈任务清空后会先从Microtasks中取任务,Microtasks中执行完以后才会执行task中的任务。segmentfault
所以一个event loop主要流程以下:api
流程图以下:浏览器
再仔细理解一下开头列出的代码:网络
for (macroTask of macroTaskQueue) { // 1. Handle current MACRO-TASK handleMacroTask(); // 2. Handle all MICRO-TASK for (microTask of microTaskQueue) { handleMicroTask(microTask); } }
对于VUE这类web2.0框架而言,最主要作的应该仍是把对data的修改映射到DOM上。若是只修改一个数据,就刷新一次DOM,那么在一个同步过程当中,同时修改好几个数据,必然会致使屡次渲染。这确定是不可取的。
从上面的event loop咱们了解到,一个event loop对应一次render。理想情况固然就是,一次event loop产生的全部改动最好再render以前将DOM都先更新好。这样在一个周期中就能够只render一次。app
从这点需求出发很容易发现,microtask queue很符合要求。框架
实际代码中,不论是鼠标点击仍是键盘输入或是网络时间,触发了哪些方法,这些触发均可以当作开启一个event loop。这些触发形成的任何修改都放到microtask queue中,就能够保证在这一轮的evnet loop走到render ui时能够拿到最新的DOM。
要说明的是,这里的render并非维护虚拟DOM,也不是把虚拟DOM的变化投射到真实DOM上。而是将真实DOM更新到UI的过程。
这么说是由于:Event Loop 并非在 ECMAScript 标准中定义的,而是在 HTML 标准中定义的:
To coordinate events, user interaction, scripts, rendering, networking, and so forth...
在 JavaScript Engine 中(以 V8 为例),只是实现了 ECMAScript 标准,而并不关心什么 Event Loop。也就是说 Event Loop 是属于 JavaScript Runtime 的,是由宿主环境提供的(好比浏览器)。
浏览器可不会关心什么虚拟DOM。只负责DOM改变后渲染UI。
同上: 在开启一个event loop后,若是将任务放到task queue中,那么这个task任务只会在本轮Event loop结束后才会执行,并开启新一轮event loop。这无疑会致使两次render UI。
实际上,尤大为了修复一些bug,曾经将VUE.nexttick用task queue实现。可是致使了很明显的性能问题。
能够看看两个列子: 例一 , 例二
两个fiddle的实现如出一辙,就是让那个绝对定位的黄色元素起到一个fixed定位的效果:绑定scroll事件,每次滚动的时候,计算当前滚动的位置并更改到那个绝对定位元素的top属性上去。你们本身试试滚动几下,对比下效果,你就会发现第一个fiddle中的黄元素是稳定不动的,fixed很好。然后一个fiddle中就有问题了,黄色元素上下晃动,彷佛跟不上咱们scroll的节奏,总要慢一点,虽然最后停下滚动时位置是对的。
上述两个例子实际上是在这个issue中找到的,第一个jsfiddle使用的版本是Vue 2.0.0-rc.6,这个版本的nextTick实现是采用了MO,然后由于IOS9.3的WebView里的MO有bug,因而尤雨溪更改了实现,换成了window.postMessage,也就是后一个fiddle所使用的Vue 2.0.0-rc.7。后来尤雨溪了解到window.postMessage是将回调放入的macrotask 队列。这就是问题的根源了。