vue-analysis
vue-analysis copied to clipboard
Event Loop疑问请教
你好,我在掘金上看到一篇文章说的Event Loop是宏任务执行完成后再执行微任务, ui更新是在宏任务结束后执行。同时下面 阮一峰老师链接点进去后也是说的是这样。
但我在另一篇文章说的是先执行微任务再执行宏任务, ui更新是在微任务结束后执行。在学习您vuejs核心解密过程中了解到您,希望您有空的时候回复下
观点1:
掘金 https://juejin.im/post/5d5b4c2df265da03dd3d73e5
下面阮一峰老师链接 https://www.cnblogs.com/dailc/p/8325991.html
观点2:
掘金 https://juejin.im/post/5d57994ef265da03bd051969
下面参考链接 https://github.com/aooy/blog/issues/5
参考这个就好了:https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model
我的观点也是和第一个一致,另外这个图也恰好说明这一点
先执行宏任务,再执行微任务,然后更新视图。
好的。谢谢大佬!
发现很多文章依然写的先执行微任务,再执行宏任务,坑爹啊~
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/ 后来我经过思考应该是先执行微任务(promise)再执行宏任务(setTimeout)了,起码在chrome中是如此,可看上面链接。 如果把同步代码看成宏任务的话,那样说先执行宏任务再执行微任务倒也说得通。 具体更新视图时机应该是在微任务后。从vue中nextTick源码也能验证到,最新2.6.10改为promise优先也是为了解决一些视图不刷新的问题。具体源码里面有注释。 @ustbhuangyi 大佬能否再具体解析下。 @udbmnm 具体感觉等大佬再解释吧。暂时我认为我的理解是对的。
同步代码不就是宏任务么
不清楚 感觉不是吧
@MaxsionLiu 问题分两个,首先不谈vue的nextTick来说事件循环,你看的图之所以说microtask先执行是因为忽略了currently-running-task,其实currently-running-task就是task,也就是macrotask。现在假设我们currently-running-task不属于macrotask,也就是第一次循环时,执行栈里面的macrotask是空的,macrotask才被放入宏任务队列,所以第一次看起来像是microtask先执行,但是下一次循环一旦取出macrotask到主执行栈,一定是macrotask先执行然后在清空microtask,所以macrotask先执行是毫无疑问的。 现在再谈vue的nextTick的问题,你可以看这里https://github.com/DDFE/DDFE-blog/issues/24 有解释,源码分析里面所讲的版本是 Vue.js 2.5.17-beta.0,也就是把flushCallbacks放到macrotask中去执行
我们在有之前的知识背景,再理解 nextTick 的实现就不难了,这里有一段很关键的注释:在 Vue 2.4 之前的版本,nextTick 几乎都是基于 micro task 实现的,但由于 micro task 的执行优先级非常高,在某些场景下它甚至要比事件冒泡还要快,就会导致一些诡异的问题,如 issue #4521、#6690、#6566;但是如果全部都改成 macro task,对一些有重绘和动画的场景也会有性能影响,如 issue #6813。所以最终 nextTick 采取的策略是默认走 micro task,对于一些 DOM 交互事件,如 v-on 绑定的事件回调函数的处理,会强制走 macro task。 这个强制是怎么做的呢,原来在 Vue.js 在绑定 DOM 事件的时候,默认会给回调的 handler 函数调用 withMacroTask 方法做一层包装,它保证整个回调函数执行过程中,遇到数据状态的改变,这些改变都会被推到 macro task 中。 对于 macro task 的执行,Vue.js 优先检测是否支持原生 setImmediate,这是一个高版本 IE 和 Edge 才支持的特性,不支持的话再去检测是否支持原生的 MessageChannel,如果也不支持的话就会降级为 setTimeout 0。
最后再说你提到文章里面的microtask优先,作者说看的版本是2.6.10,这个版本我没看,但是看贴的代码可以看出 如果支持Promise 和MutationObserver 就使用Promise 和MutationObserver ,因为Promise和MutationObserver都属于microtask,所以他说microtask优先也是对的,但这意思并不是说Event Loop里面microtask就是先执行的,只是说当前循环下解决完microtask的任务。
最后,我看到 这里提到的有点疑问 :
为何把 Vue.js 降级到 2.4+ 就没问题呢,因为 Vue.js 2.5 之前的 nextTick 都是优先使用 microtask 的,那么 audio 播放的时机实际上还是在当前 tick,所以当然不会有问题。
难道说2.5之前是microtask优先然后2.5改成了macrotask,然后2.6又变成了microtask优先?
这还需要看新版源码确认下,不过原理清楚了,怎么改都不是问题
刚看了下2.6发布的变动 有提到:
在 2.5 当中我们引入了一个改动,使得当一个 v-on DOM 事件侦听器触发更新时,会使用 Macrotask 而不是 Microtask 来进行异步缓冲。这原本是为了修正一类浏览器的特殊边际情况导致的 bug 才引入的,但这个改动本身却导致了更多其它的问题。在 2.6 里面我们对于原本的边际情况找到了更简单的 fix,因此这个 Macrotask 的改动也就没有必要了。现在 nextTick 将会统一全部使用 Microtask。如果你对具体的细节感兴趣,可以看这里。
OK 受教了。
同步代码不就是宏任务么
建议老师在文档上注明【同步代码就是宏任务】 现在文档是这样写的: 在浏览器环境中,常见的 macro task 有 setTimeout、MessageChannel、postMessage、setImmediate;常见的 micro task 有 MutationObsever 和 Promise.then。
所以很容易在setTimout 和 Promise.then上面产生疑惑, 最后参考了这篇文章:https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/ 来自一个踩过坑的人~