vue-analysis icon indicating copy to clipboard operation
vue-analysis copied to clipboard

Event Loop疑问请教

Open ammikeya opened this issue 5 years ago • 10 comments

你好,我在掘金上看到一篇文章说的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

ammikeya avatar Aug 21 '19 01:08 ammikeya

参考这个就好了:https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model 我的观点也是和第一个一致,另外这个图也恰好说明这一点 image 先执行宏任务,再执行微任务,然后更新视图。

ustbhuangyi avatar Aug 21 '19 05:08 ustbhuangyi

好的。谢谢大佬!

ammikeya avatar Aug 21 '19 05:08 ammikeya

发现很多文章依然写的先执行微任务,再执行宏任务,坑爹啊~

udbmnm avatar Aug 29 '19 08:08 udbmnm

https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/ 后来我经过思考应该是先执行微任务(promise)再执行宏任务(setTimeout)了,起码在chrome中是如此,可看上面链接。 如果把同步代码看成宏任务的话,那样说先执行宏任务再执行微任务倒也说得通。 具体更新视图时机应该是在微任务后。从vue中nextTick源码也能验证到,最新2.6.10改为promise优先也是为了解决一些视图不刷新的问题。具体源码里面有注释。 @ustbhuangyi 大佬能否再具体解析下。 @udbmnm 具体感觉等大佬再解释吧。暂时我认为我的理解是对的。

ammikeya avatar Aug 30 '19 06:08 ammikeya

同步代码不就是宏任务么

ustbhuangyi avatar Aug 30 '19 10:08 ustbhuangyi

不清楚 感觉不是吧

ammikeya avatar Aug 31 '19 09:08 ammikeya

@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。如果你对具体的细节感兴趣,可以看这里

udbmnm avatar Sep 01 '19 14:09 udbmnm

OK 受教了。

ammikeya avatar Sep 02 '19 00:09 ammikeya

同步代码不就是宏任务么

建议老师在文档上注明【同步代码就是宏任务】 现在文档是这样写的: 在浏览器环境中,常见的 macro task 有 setTimeout、MessageChannel、postMessage、setImmediate;常见的 micro task 有 MutationObsever 和 Promise.then。

所以很容易在setTimout 和 Promise.then上面产生疑惑, 最后参考了这篇文章:https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/ 来自一个踩过坑的人~

tangxiao258 avatar Aug 10 '20 07:08 tangxiao258