点击触发事件和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 事件执行

不同点在于:

  • 页面点击触发,此时执行栈为空,会向宏任务队列添加两个 onclick,执行第一个时产生 promise、mutate,添加到微任务队列,产生 timeout,添加到宏任务队列,也就是第二个 onclick 后边,然后执行栈为空,执行微任务对列,打印 promise、mutate,微任务对列边空,执行宏任务对列,即第二个 onclick,重复以上。
  • js 触发最关键的不同在于他会在最后多一行代码,也就是执行栈是有 script 的,执行 inner.click(),打印 innerclick,添加 promise、mutate 到微任务队列,添加 timeout 到宏任务队列,然后检测执行栈是否为空,发现冒泡触发的 outer.click() 事件,执行栈仍然不为空,打印 outerclick,添加 promise 到微任务队列,添加 mutate 时,相同的一个已经被添加,所以跳过,后边继续正常执行。

你可能感兴趣的:(点击触发事件和JS触发事件在EventLoop上的区别)