点击触发事件和JS触发事件在EventLoop上的区别

问题
话不多说,直接上代码分析:


var outer = document.querySelector('.outer'); var inner = document.querySelector('.inner'); new MutationObserver(function () { console.log('mutate'); }).observe(outer, { attributes: true })function onClick() { console.log('click'); setTimeout(function () { console.log('timeout') }, 0); Promise.resolve().then(function () { console.log('promise') }); outer.setAttribute('data-random', Math.random()); }outer.addEventListener('click', onClick); inner.addEventListener('click', onClick); // inner.click();

对于以上代码,直接在页面点击触发,会打印出什么样的日志顺序?
如果在代码最后使用 js 触发:inner.click(),又会是怎样的打印顺序呢?
解答
点击 outer 的打印结果:click promise mutate timeout
就是正常的 eventloop 顺序,着重说 inner 的结果。
点击 inner 的打印结果:click promise mutate click promise mutate timeout timeout
inner.click() 触发 inner 的打印结果:click click promise mutate promise timeout timeout
为什么会产生这样的区别?
触发 inner 会有事件冒泡,同时触发 outer 事件,所以会有两次 onclick 事件执行
【点击触发事件和JS触发事件在EventLoop上的区别】不同点在于:
  • 页面点击触发,此时执行栈为空,会向宏任务队列添加两个 onclick,执行第一个时产生 promise、mutate,添加到微任务队列,产生 timeout,添加到宏任务队列,也就是第二个 onclick 后边,然后执行栈为空,执行微任务对列,打印 promise、mutate,微任务对列边空,执行宏任务对列,即第二个 onclick,重复以上。
  • js 触发最关键的不同在于他会在最后多一行代码,也就是执行栈是有 script 的,执行 inner.click(),打印 innerclick,添加 promise、mutate 到微任务队列,添加 timeout 到宏任务队列,然后检测执行栈是否为空,发现冒泡触发的 outer.click() 事件,执行栈仍然不为空,打印 outerclick,添加 promise 到微任务队列,添加 mutate 时,相同的一个已经被添加,所以跳过,后边继续正常执行。

    推荐阅读