Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Event Loop疑问请教 #63

Open
ammikeya opened this issue Aug 21, 2019 · 10 comments
Open

Event Loop疑问请教 #63

ammikeya opened this issue Aug 21, 2019 · 10 comments

Comments

@ammikeya
Copy link

你好,我在掘金上看到一篇文章说的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
下面参考链接 aooy/blog#5

@ustbhuangyi
Copy link
Owner

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

@ammikeya
Copy link
Author

好的。谢谢大佬!

@udbmnm
Copy link

udbmnm commented Aug 29, 2019

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

@ammikeya
Copy link
Author

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

@ustbhuangyi
Copy link
Owner

同步代码不就是宏任务么

@ammikeya
Copy link
Author

不清楚 感觉不是吧

@udbmnm
Copy link

udbmnm commented Sep 1, 2019

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

@ammikeya
Copy link
Author

ammikeya commented Sep 2, 2019

OK 受教了。

@tangxiao258
Copy link

同步代码不就是宏任务么

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants
@udbmnm @ustbhuangyi @tangxiao258 @ammikeya and others