我正在阅读Jake Archibald关于任务,微任务等的文章。在他的示例中,我们看到"控制台日志"以基于用户的点击事件的特定顺序打印,并以编程方式触发的点击事件的不同顺序打印。我要求是否有人可以更详细地解释一下这两种情况下的点击回调执行。
.HTML
<div class="outer-test"><div class="inner-test"></div></div>
.CSS
.outer-test {
background: #D4D4D4;
padding: 25px;
width: 92px;
margin: 0 auto;
}
.inner-test {
background: #ADADAD;
padding: 46px;
width: 0;
}
JS(第一种情况(
var outer = document.querySelector('.outer-test');
var inner = document.querySelector('.inner-test');
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());
}
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
JS(第二种情况(
var outer = document.querySelector('.outer-test');
var inner = document.querySelector('.inner-test');
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());
}
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
inner.click(); //<========
供参考:https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
.click(( 会导致事件同步调度,因此调用 .click(( 的脚本仍在回调之间的堆栈中。
他看了一眼答案,但基本上就是这样。不会假装我完全理解细节,但正如我阅读的那样:由于单击是从 JavaScript 中触发的,与设置事件侦听器的代码位于同一"框架"中,并且以这种方式触发事件是同步的,因此回调直接运行,因为它不允许检查微任务队列中的突变。
在第一种情况下,"设置"结束,"帧"结束。因此,只有通过在浏览器中单击才能触发,然后这将在另一个帧中发生(异步(,并且由于此时没有其他"帧"正在运行,因此它继续并首先检查微任务队列中的突变。
上述规则确保微任务不会中断执行中期的JavaScript。
这似乎是为什么没有这样做的原因,这是有道理的,因为它都是单线程的(你知道,有点(,让那个"框架"完成是有意义的。
因此,怪癖在于以编程方式触发事件会迫使所有事件进入同步状态。