JavaScript任务调度,Macrotask和Microtasks

时间:2017-07-03 02:26:24

标签: javascript event-handling

来自Jake Archibald的博客

小提琴(点击嘿):https://jsfiddle.net/1rpzycLf/

HTML:

<div class="outer">
  <div class="inner"></div>
</div>

JS:

// Let's get hold of those elements
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');

// Let's listen for attribute changes on the
// outer element
new MutationObserver(function() {
  console.log('mutate');
}).observe(outer, {
  attributes: true
});

// Here's a click listener…
function onClick() {
  console.log('click');

  setTimeout(function() {
    console.log('timeout');
  }, 0);

  Promise.resolve().then(function() {
    console.log('promise');
  });

  outer.setAttribute('data-random', Math.random());
}

// …which we'll attach to both elements
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);

inner div运行此作品时,我得到的结果是

click
mutate
click
mutate
promise
promise
timeout
timeout

我正在努力看到这是怎么回事。 执行应该是

  1. First Handler(Macrotask)
  2. 处理全部 Microtasks
  3. Second Handler(Macrotask)
  4. 处理全部 Microtasks
  5. SetTimeout(Macrotask)
  6. SetTimeout(Macrotask)
  7. 考虑到这一点,我希望输出日志:

    click
    promise
    mutate
    click
    promise
    mutate
    timeout
    timeout
    

    不确定为什么只有在处理了两个点击事件处理程序后才会执行promises。理想情况下,第一个承诺应该在第一个mutate之后执行,但我们可以看到事实并非如此。谁知道为什么? (使用firefox 54.0)

1 个答案:

答案 0 :(得分:1)

当您点击该元素时,您自然会先输出click,因为您上面有一个点击事件处理程序,并且单词的日志点击了&#39;在click事件处理函数中发生的第一个事件。

接下来是setTimeout(function() {}, 0);。这会暂停JavaScript的执行,就像C中的线程/进程产量一样。它直到稍后才执行,所以我们稍后会再回过头来看。

因为您实际上没有做任何承诺,它会立即解决,退出第二

突变发生在第三,因为DOM是从上到下读取的,并且您在promise解析后直接改变data-random属性。

最后,现在DOM已经完成读取,超时结束第四次

由于 a separate execution context timeout从内部<div>被记录两次,从而被调用。这可以通过以下事实看出:console.log(this)中的onclick 提供与setTimeout(function() {console.log(this)}, 0);相同的上下文。由于 bubbling 与延迟的setTimeout一起使用,它会尝试首先从孩子<div>开始,然后从父母开始 <div>(您在技术上点击了它)。

因此,您最终得到:

click
promise
mutate
timeout
timeout

clickpromisemutate日志将始终一个接一个地乘以您同时点击的元素数量。 timeout日志将始终排在最后。

&#13;
&#13;
// Let's get hold of those elements
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');

// Let's listen for attribute changes on the
// outer element
new MutationObserver(function() {
  console.log('mutate');
}).observe(outer, {
  attributes: true
});

// Here's a click listener…
function onClick() {
  console.clear(); // Added for clarity
  console.log('click');

  setTimeout(function() {
    console.log('timeout');
  }, 0);

  Promise.resolve().then(function() {
    console.log('promise');
  });

  outer.setAttribute('data-random', Math.random());
}

// …which we'll attach to both elements
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
&#13;
<div class="outer">Outer
  <div class="inner">Inner</div>
</div>
&#13;
&#13;
&#13;

请注意,不同的浏览器会以不同的方式处由于代码逻辑,我假设 Chrome(我的答案基于此)正确处理此问题。

Firefox在承诺之前处理突变:

click
mutate
promise
promise
timeout
timeout

Edge在承诺之前处理突变和超时:

click
mutate
timeout
promise
timeout
promise

IE根本无法处理承诺,导致语法错误。

希望这有帮助! :)