一、關於javascript
javascript是一門 單線程 語言,在最新的HTML5中提出了Web-Worker,但javascript是單線程這一核心仍未改變。因此一切javascript版的"多線程"都是用單線程模擬出來的,一切javascript多線程都是紙老虎!
二、javascript事件循環
既然js是單線程,那就像只有一個窗口的銀行,客戶須要排隊一個一個辦理業務,同理js任務也要一個一個順序執行。若是一個任務耗時過長,那麼後一個任務也必須等着。那麼問題來了,假如咱們想瀏覽新聞,可是新聞包含的超清圖片加載很慢,難道咱們的網頁要一直卡着直到圖片徹底顯示出來?所以聰明的程序員將任務分爲兩類:javascript
JS分爲同步任務和異步任務
同步任務都在主線程上執行,造成一個執行棧
主線程以外,事件觸發線程管理着一個任務隊列,只要異步任務有了運行結果,就在任務隊列之中放置一個事件。
一旦執行棧中的全部同步任務執行完畢(此時JS引擎空閒),系統就會讀取任務隊列,將可運行的異步任務添加到可執行棧中,開始執行java
事件循環是經過任務隊列的機制來進行協調的。一個 Event Loop 中,能夠有一個或者多個任務隊列(task queue),一個任務隊列即是一系列有序任務(task)的集合;每一個任務都有一個任務源(task source),源自同一個任務源的 task 必須放到同一個任務隊列,從不一樣源來的則被添加到不一樣隊列。 setTimeout/Promise 等API即是任務源,而進入任務隊列的是他們指定的具體執行任務。程序員
宏任務:promise
macro)task(又稱之爲宏任務),能夠理解是每次執行棧執行的代碼就是一個宏任務(包括每次從事件隊列中獲取一個事件回調並放到執行棧中執行)。 瀏覽器爲了可以使得JS內部(macro)task與DOM任務可以有序的執行,會在一個(macro)task執行結束後,在下一個(macro)task 執行開始前,對頁面進行從新渲染,流程以下: (macro)task->渲染->(macro)task->... (macro)task主要包含:script(總體代碼)、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(Node.js 環境)
微任務:瀏覽器
microtask(又稱爲微任務),能夠理解是在當前 task 執行結束後當即執行的任務。也就是說,在當前task任務後,下一個task以前,在渲染以前。 因此它的響應速度相比setTimeout(setTimeout是task)會更快,由於無需等渲染。也就是說,在某一個macrotask執行完後,就會將在它執行期間產生的全部microtask都執行完畢(在渲染前)。 microtask主要包含:Promise.then、MutaionObserver、process.nextTick(Node.js 環境)
運行機制:多線程
在事件循環中,每進行一次循環操做稱爲 tick,每一次 tick 的任務處理模型是比較複雜的,但關鍵步驟以下: 執行一個宏任務(棧中沒有就從事件隊列中獲取) 執行過程當中若是遇到微任務,就將它添加到微任務的任務隊列中 宏任務執行完畢後,當即執行當前微任務隊列中的全部微任務(依次執行) 當前宏任務執行完畢,開始檢查渲染,而後GUI線程接管渲染 渲染完畢後,JS線程繼續接管,開始下一個宏任務(從事件隊列中獲取)
流程圖以下:異步
兩道題 讓你所有理解事件循環async
console.log('1') setTimeout(function() { console.log('2') process.nextTick(function() { console.log('3') }) new Promise(function(resolve) { console.log('4') resolve() }).then(function() { console.log('5') }) }) process.nextTick(function() { console.log('6') }) new Promise(function(resolve) { console.log('7') resolve() }).then(function() { console.log('8') }) setTimeout(function() { console.log('9') process.nextTick(function() { console.log('10') }) new Promise(function(resolve) { console.log('11') resolve() }).then(function() { console.log('12') }) }) 第二題 async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2() { console.log('async2'); } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0) async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }); console.log('script end'); 注意: async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } 等同於 async function async1() { console.log('async1 start'); Promise.resolve(async2()).then(() => { console.log('async1 end'); }) }