简体   繁体   English

JavaScript任务计划,宏任务和微任务

[英]JavaScript task scheduling, Macrotask and Microtasks

From Jake Archibald 's blog 杰克·阿奇博尔德Jake Archibald )的博客

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

HTML: HTML:

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

JS: 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);

When running this piece for the inner div the results I get are 当为inner div运行此片段时,我得到的结果是

click
mutate
click
mutate
promise
promise
timeout
timeout

and I am struggling to see how this is the case. 我正在努力看情况如何。 The Execution should be 执行应该是

  1. First Handler (Macrotask) 第一处理程序(宏任务)
  2. Process All Microtasks 处理所有微任务
  3. Second Handler (Macrotask) 第二处理程序(宏任务)
  4. Process All Microtasks 处理所有微任务
  5. SetTimeout (Macrotask) SetTimeout(宏任务)
  6. SetTimeout (Macrotask) SetTimeout(宏任务)

With this in mind, I am expecting the log to output instead: 考虑到这一点,我期望输出日志:

click
promise
mutate
click
promise
mutate
timeout
timeout

Not sure why promises are being executed only after two of the click's event handler has been processed. 不确定为什么仅在两次单击事件处理程序处理后才执行promises The very first promise should ideally execute after the first mutate but we can see that it is clearly not the case. 理想情况下,第一个承诺应该在第一个mutate之后执行,但是我们可以看到事实显然并非如此。 Anyone know why? 有人知道为什么吗? (Using firefox 54.0) (使用firefox 54.0)

When you click on the element, you naturally get click outputted first, because you have a click event handler on it, and the log of the word 'click' in the first thing to happen in the click event handler function. 当您单击该元素时,自然会首先输出click ,因为您上面有一个click事件处理程序,并且单击事件处理函数中首先发生的单词“ click”的日志。

Next up is setTimeout(function() {}, 0); 接下来是setTimeout(function() {}, 0); . This pauses JavaScript's execution, and is like a thread/process yield in C. It doesn't execute until later, so we'll come back to that in a bit. 这暂停了JavaScript的执行,就像C语言中的线程/进程产量一样。它直到稍后才执行,因此我们将稍等一下。

Because you're not actually doing anything with the promise, it resolves instantly, logging out second . 因为你没有真正做什么用的承诺,它解决了瞬间, 第二注销。

The mutation happens third because the DOM is read top-to-bottom, and you are mutating the data-random attribute directly after the promise resolves. 因为DOM是从上到下读取的,所以突变发生在第三位 ,并且您在promise解析之后直接对data-random属性进行突变。

Finally, now that the DOM has finished being read, the timeout finishes fourth . 最终,既然DOM已经完成读取,那么超时将排在第四位

The timeout gets logged twice from the inner <div> due to a separate execution context to where it was called from. timeout从内部<div>被记录两次,这是因为从其调用的地方有单独的执行上下文 This can be seen by the fact that console.log(this) inside an onclick does not provide the same context as setTimeout(function() {console.log(this)}, 0); 这可以通过这样的事实可以看出, console.log(this)内的onclick 提供相同的上下文中setTimeout(function() {console.log(this)}, 0); . Due to bubbling in conjunction with the delayed setTimeout , it attempts to fire first from the child <div> , and then also from the parent <div> (which you technically clicked on). 由于冒泡以及延迟的setTimeout ,它首先尝试从子<div>触发,然后又从 <div> (从技术上单击)触发。

Thus, you end up with: 因此,您最终得到:

click
promise
mutate
timeout
timeout

The click , promise and mutate logs will always come one after another, multiplied by the number of elements that you are clicking on simultaneously. clickpromisemutate日志将总是一个接一个地乘以您同时单击的元素数。 The timeout logs will always come last. timeout日志将始终排在最后。

 // 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); 
 <div class="outer">Outer <div class="inner">Inner</div> </div> 

Note that different browsers process these differently. 请注意,不同的浏览器对这些内容的处理方式也不同。 I'd assume that Chrome (which my answer is based off on) handles this correctly, due to the code logic. 认为 Chrome浏览器(其中我的答案是基于关闭的)正确处理此,由于代码逻辑。

Firefox handles mutations before promises: Firefox处理承诺之前的突变:

click
mutate
promise
promise
timeout
timeout

Edge handles both mutations and timeouts before promises: Edge在承诺之前处理变异和超时:

click
mutate
timeout
promise
timeout
promise

IE can't handle promises at all, throwing a syntax error. IE根本无法处理承诺,会引发语法错误。

Hope this helps! 希望这可以帮助! :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM