衆所周知,爲了與瀏覽器進行交互,Javascript是一門非阻塞單線程腳本語言。
爲什麼單線程? 由於若是在DOM操做中,有兩個線程一個添加節點,一個刪除節點,瀏覽器並不知道以哪一個爲準,因此只能選擇一個主線程來執行代碼,以防止衝突。雖然現在添加了webworker等新技術,但其依然只是主線程的子線程,並不能執行諸如I/O類的操做。長期來看,JS將一直是單線程。web
爲什麼非阻塞?由於單線程意味着任務須要排隊,任務按順序執行,若是一個任務很耗時,下一個任務不得不等待。因此爲了不這種阻塞,咱們須要一種非阻塞機制。這種非阻塞機制是一種異步機制,即須要等待的任務不會阻塞主執行棧中同步任務的執行。這種機制是以下運行的:promise
執行棧(execution context stack)
任務隊列(task queue)
。任務隊列
,任務隊列中的異步任務(即以前等待任務的回調結果)會塞入主執行棧,事件循環(Event Loop)
用一張圖展現這個過程:瀏覽器
在實際狀況中,上述的任務隊列(task queue)
中的異步任務分爲兩種:微任務(micro task)
和宏任務(macro task)
。dom
Promises(瀏覽器實現的原生Promise)
、MutationObserver
、process.nextTick
setTimeout
、setInterval
、setImmediate
、I/O
、UI rendering
script(總體代碼)
即一開始在主執行棧中的同步代碼本質上也屬於macrotask,屬於第一個執行的taskmicrotask和macotask執行規則:異步
下面來個簡單例子:oop
console.log(1); setTimeout(function() { console.log(2); }, 0); new Promise(function(resolve,reject){ console.log(3) resolve() }).then(function() { console.log(4); }).then(function() { console.log(5); }); console.log(6);
一步一步分析以下:ui
再來一個複雜的例子:spa
// 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);
假設咱們建立一個有裏外兩部分的正方形盒子,裏外都綁定了點擊事件,此時點擊內部,代碼會如何執行?一步一步分析以下:線程