事件循環--單線程原理
阿新 • • 發佈:2018-06-28
onclick 由於 分類 oms 再次 htm 顯示器 document 主線程
http://www.haorooms.com/post/js_xiancheng
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
任務隊列
- 主線程:正在執行的代碼,會生成函數調用棧。
- macro-task(宏任務,新名:task)包括:script(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering。
- micro-task(微任務,新名:jobs)包括: process.nextTick, Promise, Object.observe(已廢棄), MutationObserver(html5新特性,隊列中只能有一個)
任務分類
- 同步任務,語句只按語句先後順序執行,前面未執行完,不會執行後面語句。
- 異步任務,語句不在語句先後順序上執行,執行到該代碼時,加入到相應任務隊列,延後執行。
單線程
主線程從 script (整體代碼)開始第一次循環。之後全局上下文進入函數調用棧。直到調用棧清空(只剩全局),然後執行所有的 jobs。當所有可執行的 jobs 執行完畢之後。循環再次從 task 開始,找到其中一個任務隊列執行完畢,然後再執行所有的 jobs,這樣一直循環下去。
註意事項
- setTimeout 最小間隔不能低於 4 毫秒,否則會自動增加。
- DOM 的渲染每 16 毫秒執行一次,因為顯示器是 60 Hz,16ms 刷新一次。
- process.nextTick 任務會在 jobs 裏單獨維護一個隊列,並且在其他 jobs 任務之前執行。
- 冒泡事件會直接在子元素事件執行完成後,插入在主線程中。如果主線程不為空,那麽會優先於 jobs 執行。
經典示例
示例詳解:https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
通過鼠標點擊
<div class="outer"> <div class="inner"></div> </div> // 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); // 輸出結果 click mutate click mutate promise promise timeout timeout
進階--通過js執行
<div class="outer">
<div class="inner"></div>
</div>
// 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.click();
// 輸出結果
click
click
mutate
promise
promise
timeout
timeout
由於點擊事件是 js 執行的,inner 的 onClick 函數執行完成時,inner.click() 語句的作用域還沒有退棧,主線程調用棧不是空的,導致 jobs 隊列任務不會執行,mutate 和 promise 語句都未能在事件循環中執行到。從而執行了 outer 的 onClick 函數。outer 的 onClick 函數執行完成後,inner.click() 語句才退棧,繼而執行 jobs 的任務。
只有一個 mutate 是由於 jobs 隊列中,只能有一個 MutationObserver 任務,第二次創建時,前一個 MutationObserver 任務沒有執行,顧不再創建。
事件循環--單線程原理