說javascript
運行機制以前,先看一段代碼:javascript
console.log(1) Promise.resolve().then(function () { console.log(2) }) new Promise(function(resolve, reject){ console.log(3) resolve() }).then(function () { console.log(4) setTimeout(function () { console.log(5) }) }) console.log(6) setTimeout(function () { Promise.resolve().then(function () { console.log(7) setTimeout(function () { console.log(8) }) }) })
若是你看到這段代碼,並知道正確的輸出順序。那說明你對這塊掌握的差很少了。(直接翻到最後看結果)java
好,言歸正傳。ajax
在說是怎麼運行的以前,先看幾個概念。promise
執行上下文簡單來講就是一個執行環境。它有全局環境、函數環境和eval函數環境之分。它會在javascript
引擎執行你的腳本的時候去建立。瀏覽器
執行棧也就是常說的調用棧,它是一種擁有LIFO(後進先出)的數據結構。它會存儲代碼運行時建立的執行上下文數據結構
javasript
中的任務分爲微任務和宏任務兩種,這兩種任務的執行時機是不一樣的,所以區分js中哪些是宏任務,哪些是微任務則十分重要。咱們常見的宏任務有:script任務
、setTimeout
、ajax
等,常見的微任務比較典型的是:Promise.resolve().then()
、process.nextTick
、MutationObserver
等。函數
js是單線程的,也就是說它一次僅能處理一個任務。但js所在的宿主環境,也就是咱們所說的瀏覽器並非單線程的(這裏宿主環境僅討論瀏覽器)。它在遇到一些任務時,好比說setTimeout
、event listener
等。它會告訴瀏覽器:老兄幫個忙,事成後通知我一聲,小弟我先幹別的事去了。瀏覽器會迴應說:交給我吧,小老弟,事成後我放到任務隊列,本身去取啊。因而,js開始執行script任務,執行完了就開始檢查有沒有微任務啊,沒有的話就從任務隊列開始取宏任務執行,每執行完一次宏任務,就去看看有沒有微任務,有的話就執行完成,再執行宏任務,如此往復。以下:oop
瞭解了這幾個概念,再來看看javascript
是怎麼執行代碼的就比較輕鬆愉快了。開始吧spa
console.log(1) Promise.resolve().then(function () { console.log(2) }) new Promise(function(resolve, reject){ console.log(3) resolve() }).then(function () { console.log(4) setTimeout(function () { console.log(5) }) }) console.log(6) setTimeout(function () { Promise.resolve().then(function () { console.log(7) setTimeout(function () { console.log(8) }) }) })
js引擎在執行這段代碼的時候,首先將全局執行上下文壓入棧中:線程
而後呢,在執行的時候會碰到console.log
函數,將它壓入棧中:
這個時候,直接執行console
函數,並輸出1
。而後console
函數出棧:
繼續往下執行,碰到了Promise.resolve().then()
,先將Promise.resolve().then()
壓入棧中(這裏,我爲了圖方便就把它當作總體了,否則得畫不少圖)。
而後執行Promise.resolve().then()
,前面說過,這個then()
函數是個微任務,它會將傳入給它的回調函數加入到微任務隊列中。
而後Promise.resolve().then()
就出棧了。
接着執行,遇到promise
的構造函數,這個構造函數是一個宏任務,會直接將傳遞給它的函數壓入棧中。
執行console
函數並輸出3
,執行完,console
函數出棧,接着執行resolve()
函數,並出棧。
而後繼續執行then
函數,將傳遞給then
函數的參數函數放到微任務隊列中:
繼續來,繼續往下執行。碰到console.log(6)
,二話不說,直接壓入棧中,執行,輸出6
,出棧,一鼓作氣。
接着,引擎碰到了setTimeout
函數,這傢伙是個宏任務,但同時它會將傳遞給它的函數,加入到任務隊列中:
好了,到此第一波宏任務就所有執行完畢。接着,引擎就會去看一下微任務隊列中有沒有任務,若是有的話,執行它們。
如今看到的是,微任務隊列中有兩個任務。按照隊列的先入先出規則,先從function () {console.log(2)}
開始執行。先是函數入棧,而後執行函數,輸出2
,而後函數出棧。
接着執行下面這段代碼:
console.log(4) setTimeout(function () { console.log(5) })
先從console.log(4)
開始,先將它入棧,而後執行它,輸出4
,而後函數出棧。
接着執行:
setTimeout(function () { console.log(5) })
將
function () { console.log(5) }
加入到任務隊列中去
先執行:
function(){ Promise.resolve().then(function () { console.log(7) setTimeout(function () { console.log(8) }) }) }
這裏執行這個函數的時候遇到一個微任務,將這個微任務添加到微任務隊列,以下:
這批次的宏任務就執行完畢了,接着就回去檢查微任務隊列中有沒有待執行的任務。一看還真有兩個小可愛等待執行,因而沒什麼好說的,直接擰出去就執行
先是執行console.log(7)
,而後輸出7
。接着執行setTimeout
,將傳遞給他的任務添加到任務隊列中去:
最後就剩這兩個函數了,按照隊列的先入後出一次執行吧,輸出5
和8
。
好了,最後的結果就是1,3,6,2,4,7,5,8
。你寫對了了嗎?