JavaScript運行機制

JavaScript運行機制,重點有兩點:

  • JavaScript是一門單線程語言
  • Event Loop(事件循環)是JavaScript的執行機制

既然說JavaScript是單線程,那就是在執行代碼的時候是從上往下執行的,先來看一段代碼:javascript

setTimeout(function(){
    console.log('定時器開始')
});
new Promise(function(resolve){
    console.log('Promise開始');
    resolve();
}).then(function(){
    console.log('執行then函數')
});
console.log('代碼執行結束');
複製代碼

輸出結果:html

console.log("A");
while(true){ }
console.log("B");
請問最後的輸出結果是什麼?
複製代碼

若是你的回答是A,恭喜你答對了,由於這是同步任務,程序由上到下執行,遇到while()死循環,下面語句就沒辦法執行。前端

console.log("A");
setTimeout(function(){
console.log("B");
},0);
while(true){}
請問最後的輸出結果是什麼?
複製代碼

若是你的答案是A,恭喜你如今對js運行機制已經有個粗淺的認識了!題目中的setTimeout()就是個異步任務。在全部同步任務執行完以前,任何的異步任務是不會執行的html5

關於JavaScript

JavaScript是一門單線程語言,在最新的HTML5中提出了WebWorker, 但JavaScript是單線程這一核心扔未改變。因此一切JavaScript版的「多線程」都是用單線程模擬出來的,一切JavaScript多線程都是紙老虎java

JavaScript爲何是單線程的

最初設計JS是用來在瀏覽器驗證表單操控DOM元素的是一門腳本語言,若是js是多線程的,那麼兩個線程同時對一個DOM元素進行了相互衝突的操做,那麼瀏覽器的解析器是沒法執行的。node

JavaScript爲何須要異步

若是JavaScript中不存在異步,只能自上而下執行,若是上一行解析時間很長,那麼下面的代碼就會被阻塞。 對於用戶而言,阻塞就覺得着「卡死」,這樣就致使了不好的用戶體驗。好比在進行Ajax請求的時候若是沒有返回數據後面的代碼就沒辦法執行面試

JavaScript單線程又是如何實現異步的呢

JavaScript中的異步以及多線程均可以理解成爲一種「假象」,就拿h5的WebWorker來講,子線程有諸多限制,不能控制DOM,不能修改全局對象等等,一般只用來作計算作數據處理。 這些限制並無違背咱們以前的觀點,因此說是「假象」。JavaScript異步的執行機制其實就是事件循環(eventloop),理解了eventloop機制,就理解了JavaScript異步的執行機制。ajax

JavaScript的事件循環(eventloop)是怎麼運做的

事件循環eventloop運行機制 這三個術語其實說的是同一個東西, 「先執行同步操做,而後把異步操做排在事件隊列裏,等同步操做都運行完了(運行棧空閒),按順序運行事件隊列裏的內容」這樣的理解其實也沒有任何問題但若是深刻的話會引出不少其餘概念,好比event table和event queue, 咱們來看運行過程:promise

  • 首先判斷JavaScript是同步仍是異步,同步就進入主線程運行,異步就進入event table
  • 異步任務在event table中註冊事件,當知足觸發條件後,(觸發條件多是延時也多是ajax回調),被推入event queue
  • 同步任務進入主線程後一直執行,直到主線程空閒時,纔會去event queue中查看是否有可執行的異步任務,若是有就推入主線程中

那怎麼知道主線程執行棧爲空啊?JavaScript引擎存在monitoring process進程,會持續不斷的檢查主線程執行棧是否爲空,一旦爲空,就會去event queue那裏檢查是否有等待被調用的函數。瀏覽器

let data = [];
$.ajax({
    url:www.javascript.com,
    data:data,
    success:() => {
        console.log('發送成功!');
    }
})
console.log('代碼執行結束');
複製代碼

上述代碼的運行機制以下:

  • ajax進入event table,註冊回調函數success
  • 執行 console.log('代碼執行結束')
  • ajax事件完成,回調函數success進入event queue
  • 主線程從event queue讀取回調函數success並執行

會放入異步任務隊列的語句

  • setTimeout和setlnterval
  • DOM事件
  • ES6中的Promise
  • Ajax異步請求

javascript代碼運行分兩個階段

  • 預解析---把全部的函數定義提早,全部的變量聲明提早,變量的賦值不提早
  • 執行---從上到下執行(按照js運行機制)

setTimeout

setTimeout(() => {
  console.log('2秒到了')
}, 2000)
複製代碼

setTimeout是異步操做首先進入event table, 註冊的事件就是它的回調,觸發條件就是2秒以後,當知足條件回調被推入event queue,當主線程空閒時會去event queue裏查看是否有可執行的任務。

console.log(1) // 同步任務進入主線程
setTimeout(fun(),0)   // 異步任務,被放入event table, 0秒以後被推入event queue裏
console.log(3) // 同步任務進入主線程
複製代碼

一、3是同步任務立刻會被執行,執行完成以後主線程空閒去event queue(事件隊列)裏查看是否有任務在等待執行,這就是爲何setTimeout的延遲事件是0毫秒卻在最後執行的緣由

但setTimeout延時的時間有時候並非那麼準確

setTimeout(() => {
  console.log('2秒到了')
}, 2000)
wait(9999999999)
複製代碼

分析運行過程:

  • console進入Event Table並註冊,計時開始。
  • 執行sleep函數,sleep方法雖然是同步任務但sleep方法進行了大量的邏輯運算,耗時超過了2秒
  • 2秒到了,計時事件timeout完成,console進入Event queue, 可是sleep還沒執行完,主線程還被佔用,只能等着。
  • sleep終於執行完了, console終於從event queue進入了主線程執行,這個時候已經遠遠超過了2秒

其實延遲2秒只是表示2秒後,setTimeout裏的函數被推入event queue , 而event queue(事件隊列)裏的任務,只有在主線程空閒時纔會執行。 上述流程走完,咱們知道setTimeout這個函數,是通過指定時間後,把要執行的任務(本例子中爲console)加入到event queue中, 又由於單線程任務要一個一個執行,若是前面的任務須要的時間過久,那麼只能等着,致使真正的延遲時間遠遠大於2秒。 咱們還常常遇到setTimeout(fn,0)這樣的代碼,它的含義是,指定某個任務在主線程最先的空閒時間執行,意思就是不用再等多少秒了, 只要主線程執行棧內的同步任務所有執行完成,棧爲空就立刻執行。可是即使主線程爲空,0毫秒實際上也是達不到的。根據HTML的標準,最低是4毫秒

setIntval

以setIntval(fn,ms)爲例,setIntval是循環執行的,setIntval會每隔指定的時間將註冊的函數置入event queue,不是每過ms會執行一次fn,而是每過ms秒,會有fn進入event queue。須要注意一點的是,一但setIntval的回調函數fn執行時間超過了延遲事件ms,那麼就完成看不出來有時間間隔了。

除了廣義的同步任務和異步任務,咱們對任務有更精細的定義:

  • 宏任務(macro-task)script(全局任務)、setTimeout、setInterval、MessageChannel、postMessage、setImmediate、I/O、UI rendering
  • 微任務(micro-task):new Promise().then(回調)、process.nextTick、 Object.observe(已廢棄)、MutationObserver(html5新特性)

在劃分宏任務、微任務的時候並無提到async/ await,由於async/await的本質就是Promise

事件循環,宏任務,微任務的關係

一句話歸納上面的流程圖:當某個宏任務隊列的中的任務所有執行完之後,會查看是否有微任務隊列。若是有,先執行微任務隊列中的全部任務,若是沒有,就查看是否有其餘宏任務隊列。

接下來咱們看兩道例子來介紹上面流程:

Promise.resolve().then(()=>{
  console.log('Promise1')  
  setTimeout(()=>{
    console.log('setTimeout2')
  },0)
})
setTimeout(()=>{
  console.log('setTimeout1')
  Promise.resolve().then(()=>{
    console.log('Promise2')    
  })
},0)
複製代碼

最後輸出結果是Promise1,setTimeout1,Promise2,setTimeout2

  • 一開始執行棧的同步任務執行完畢,會去查看是否有微任務隊列,上題中存在(有且只有一個),而後執行微任務隊列中的全部任務輸出Promise1,同時會生成一個宏任務 setTimeout2
  • 而後去查看宏任務隊列,宏任務 setTimeout1 在 setTimeout2 以前,先執行宏任務 setTimeout1,輸出 setTimeout1
  • 在執行宏任務setTimeout1時會生成微任務Promise2 ,放入微任務隊列中,接着先去清空微任務隊列中的全部任務,輸出 Promise2
  • 清空完微任務隊列中的全部任務後,就又會去宏任務隊列取一個,這回執行的是 setTimeout2
console.log('----------------- start -----------------');
setTimeout(() => {
  console.log('setTimeout');
}, 0)
new Promise((resolve, reject) =>{
  for (var i = 0; i < 5; i++) {
    console.log(i);
  }
  resolve();  // 修改promise實例對象的狀態爲成功的狀態
}).then(() => {
  console.log('promise實例成功回調執行');
})
console.log('----------------- end -----------------');
複製代碼

那事件循環機制究竟是怎麼樣的?

不一樣類型的任務會進入對應的event queue, 好比setTime和setIntval會進入相同(宏任務)的event queue, 而Promise和process.nextTick會進入相同(微任務)的event queue. 其中,process.nextTick(callback) 相似node.js版的"setTimeout",在事件循環的下一次循環中調用 callback 回調函數。

Promise與事件循環

Promise在初始化時,傳入的函數是同步執行的,而後註冊then回調。註冊完以後,繼續往下執行同步代碼,在這以前,then的回調不會執行。同步代碼塊執行完畢後,纔會在事件循環中檢測是否有可用的promise回調,若是有,那麼執行,若是沒有,繼續下一個事件循環。

  • 1. 宏任務,微任務都是隊列,一段代碼執行時,會先執行宏任務中的同步代碼。
  • 2. 進行第一輪事件循環的時候會把所有的js腳本當成一個宏任務來運行。
  • 3. 若是執行中遇到setTimeout之類的宏任務,那麼就把這個setTimeout內部的函數推入[宏任務的隊列]中,下一輪宏任務執行時調用。
  • 4. 若是執行中遇到promise.then()之類的微任務,就會推入到[當前宏任務的微任務隊列]中,在本輪宏任務的同步代碼都執行完成後,依次執行全部的微任務。
  • 5. 第一輪事件循環中當執行徹底部的同步腳步以及微任務隊列中的事件,這一輪事件循環就結束了,開始第二輪事件循環。
  • 6. 第二輪事件循環同理先執行同步腳本,遇到其餘宏任務代碼塊繼續追加到[宏任務的隊列]中,遇到微任務,就會推入到[當前宏任務的微任務隊列]中,在本輪宏任務的同步代碼執行都完成後, 依次執行當前全部的微任務。
  • 7. 開始第三輪循環往復..

下面用代碼來深刻理解上面的機制:

setTimeout(function() {
    console.log('4')
})

new Promise(function(resolve) {
    console.log('1') // 同步任務
    resolve()
}).then(function() {
    console.log('3')
})
console.log('2')
複製代碼
  1. 這段代碼做爲宏任務,進入主線程。
  2. 先遇到setTimeout,那麼將其回調函數註冊後分發到宏任務event queue.
  3. 接下來遇到Promise, new Promise當即執行,then函數分發到微任務event queue
  4. 遇到console.log(), 當即執行
  5. 總體代碼script做爲第一個宏任務執行結束, 查看當前有沒有可執行的微任務,執行then的回調。(第一輪事件循環結束了,咱們開始第二輪循環)
  6. 從宏任務的event queue開始,咱們發現了宏任務event queue中setTimeout對應的回調函數,當即執行。 執行結果: 1-2-3-4
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')
    })
})
複製代碼
  1. 總體script做爲第一個宏任務進入主線程,遇到console.log(1)輸出1
  2. 遇到setTimeout, 其回調函數被分發到宏任務event queue中。咱們暫且記爲setTimeout1
  3. 遇到process.nextTick(),其回調函數被分發到微任務event queue中,咱們記爲process1
  4. 遇到Promise, new Promise直接執行,輸出7.then被分發到微任務event queue中,咱們記爲then1
  5. 又遇到setTimeout,其回調函數被分發到宏任務event queue中,咱們記爲setTimeout2.
  6. 如今開始執行微任務, 咱們發現了process1和then1兩個微任務,執行process1,輸出6,執行then1,輸出8, 第一輪事件循環正式結束, 這一輪的結果輸出1,7,6,8.那麼第二輪事件循環從setTimeout1宏任務開始
  7. 首先輸出2, 接下來遇到了process.nextTick(),統一被分發到微任務event queue,記爲process2
  8. new Promise當即執行,輸出4,then也被分發到微任務event queue中,記爲then2
  9. 如今開始執行微任務,咱們發現有process2和then2兩個微任務能夠執行輸出3,5。第二輪事件循環結束,第二輪輸出2,4,3,5,第三輪事件循環從setTimeout2宏任務開始
  10. 直接輸出9,跟第二輪事件循環相似,輸出9,11,10,12
  11. 完整輸出是1,7,6,8,2,4,3,5,9,11,10,12(請注意,node環境下的事件監聽依賴libuv與前端環境不徹底相同,輸出順序可能會有偏差)

若是是setTimeout裏面嵌套setTimeout, 那麼嵌套的setTimeout的宏任務要在外面的宏任務排序的後面,日後排。看個例子

new Promise(function (resolve) { 
    console.log('1')// 宏任務一
    resolve()
}).then(function () {
    console.log('3') // 宏任務一的微任務
})
setTimeout(function () { // 宏任務二
    console.log('4')
    setTimeout(function () { // 宏任務五
        console.log('7')
        new Promise(function (resolve) {
            console.log('8')
            resolve()
        }).then(function () {
            console.log('10')
            setTimeout(function () {  // 宏任務七
                console.log('12')
            })
        })
        console.log('9')
    })
})
setTimeout(function () { // 宏任務三
    console.log('5')
})
setTimeout(function () {  // 宏任務四
    console.log('6')
    setTimeout(function () { // 宏任務六
        console.log('11')
    })
})
console.log('2') // 宏任務一
複製代碼
  1. 所有的代碼做爲第一個宏任務進入主線程執行。
  2. 首先輸出1,是同步代碼。then回調做爲微任務進入到宏任務一的微任務隊列。
  3. 下面最外層的三個setTimeout分別是宏任務2、宏任務3、宏任務四按序排入宏任務隊列。
  4. 輸出2,如今宏任務一的同步代碼都執行完成了接下來執行宏任務一的微任務輸出3。 第一輪事件循環完成了
  5. 如今執行宏任務二輸出4,後面的setTimeout做爲宏任務五排入宏任務隊列。 第二輪事件循環完成了
  6. 執行宏任務三輸出5,執行宏任務四輸出6,宏任務四里面的setTimeout做爲宏任務六。
  7. 執行宏任務五輸出7,8。then回調做爲宏任務五的微任務排入宏任務五的微任務隊列。
  8. 輸出同步代碼9,宏任務五的同步代碼執行完了,如今執行宏任務五的微任務。
  9. 輸出10,後面的setTimeout做爲宏任務七排入宏任務的隊列。 宏任務五執行完成了,當前已是第五輪事件循環了。
  10. 執行宏任務六輸出11,執行宏任務七輸出12。

初步總結: 宏任務是一個棧按先入先執行的原則,微任務也是一個棧也是先入先執行。可是每一個宏任務都對應會有一個微任務棧,宏任務在執行過程當中會先執行同步代碼再執行微任務棧。

async/await是什麼

咱們建立了promise但不能同步等待它執行完成。 咱們只能經過then傳一個回調函數這樣很容易再次陷入promise的回調地獄。 實際上, async/await在底層轉換成了promise和then回調函數,也就是說, 這是promise的語法糖。每次咱們使用await, 解釋器都建立一個promise對象,而後把剩下的async函數中的操做放到then回調函數中。 async/await的實現,離不開promise. 從字面意思來理解, async是「異步」的簡寫,而await是async wait的簡寫能夠認爲是等待異步方法執行完成。

async/await用來幹什麼

用來優化promise的回調問題,被稱爲是異步的終極解決方案

async/await內部作了什麼

async函數會返回一個Promise對象,若是在函數中return一個直接量(普通變量),async會把這個直接量經過Promise.resolve()封裝成Promise對象。若是你返回了promise那就以你返回的promise爲準。await是在等待,等待運行的結果也就是返回值。await後面一般是一個異步操做(promise),可是這不表明await後面只能跟異步操做,await後面實際是能夠接普通函數調用或者直接量。 async至關於 new Promise,await至關於then

await的等待機制

若是await後面跟的不是一個promise,那await後面表達式的運算結果就是它等到的東西,若是await後面跟的是一個promise對象,await它會'阻塞'後面的diamante,等着promise對象resolve, 而後獲得resolve的值做爲await表達式的運算結果。可是此"阻塞"非彼「阻塞」,這就是await必須用在async函數中的緣由。 async函數調用不會形成"阻塞",它內部全部的「阻塞」都被封裝在一個promise對象中異步執行(這裏的阻塞理解成異步等待更合理)

async/await在使用過程當中有什麼規定

每一個async方法都返回一個promise, await只能出如今async函數中

async/await在什麼場景使用

單一的promise鏈並不能發現async/await的優點,可是若是須要處理由多個promise組成的then鏈的時候,優點就能體現出來了(Promise經過then鏈來解決多層回調的問題,如今又用async/awai來進一步優化它)

async/await如何使用

假設一個業務,分多個步驟完成,每一個步驟都是異步的且依賴於上一個步驟的結果

function myPromise(n) {
    return new Promise(resolve => {
        console.log(n)
        setTimeout(() => resolve(n+1), n)
    })
}
function step1(n) {
    return myPromise(n)
}
function step2(n) {
    return myPromise(n)
}
function step3(n) {
    return myPromise(n)
}

//若是用 Promise 實現
step1(1000)
.then(a => step2(a))
.then(b => step3(b))
.then(result => {
    console.log(result)
})

//若是用 async/await 來實現呢
async function myResult() {
    const a = await step1(1000)
    const b = await step2(a)
    const result = await step3(b)
    return result
}
myResult().then(result => {
    console.log(result)
}).catch(err => {
    // 若是myResult內部有語法錯誤會觸發catch方法
})
複製代碼

看的出來async/await的寫法更多優雅一些要比promise的連接調用更多直觀也易於維護

咱們來看在任務隊列中async/await的運行機制,先給出大概方向再經過案例來證實:

  1. async定義的是一個promise函數和普通函數同樣只要不調用就不會進入事件隊列。
  2. async內部若是沒有主動return promise, 那麼async會把函數的返回值用promise包裝
  3. await關鍵字必須出如今async函數中,await後面不是必需要跟一個異步操做,也能夠是一個普通表達式
  4. 遇到await關鍵字,await右邊的語句會被當即執行而後await下面的代碼進入等待狀態,等待await獲得結果。 await後面若是不是promise對象,await會阻塞後面的代碼,先執行async外面的同步代碼,同步代碼執行完,再回到async內部,把這個非promise的東西,做爲await表達式的結果。await後面若是是promise對象,await也會阻塞後面的代碼,先執行async外面的同步代碼,等着promise對象fulfilled,而後把resolve的參數做爲await表達式的運算結果
setTimeout(function () {
  console.log('6')
}, 0)

console.log('1')

async function async1() {
  console.log('2')
  await async2()
  console.log('5')
}

async function async2() {
  console.log('3')
}

async1()

console.log('4')
複製代碼

上述的輸出結果爲:1,2,3,4,5,6

  1. 6是宏任務在下一輪事件循環執行
  2. 先同步輸出1,而後調用async1(),輸出2
  3. await async2()會先運行async2(), 5進入等待狀態
  4. 輸出3, 這個時候先執行async函數外的同步代碼輸出4.
  5. 最後await拿到等待的結果繼續往下執行輸出5.
  6. 進入第二輪事件循環輸出6.
setTimeout(function () {
  console.log('6')
}, 0)

console.log('1')

async function async1 () {
  console.log('2')
  console.log(await async2())
  console.log('5')
}

async function async2 () {
  console.log('3')
  return '11111'
}

async1()

console.log('4')
console.log('888')
console.log('999')
複製代碼

上述的輸出結果爲:1,2,3,4,888,999,11111,5,6

測試代碼的輸出結果,看到async1函數輸出2, 立馬執行await的async2函數,輸出3, 可是沒有當即返回,而是先執行async2外面的同步代碼,最後獲得返回值11111給await async2函數

console.log('1')

async function async1() {
  console.log('2')
  await 'await的結果'
  console.log('5')
}

async1()

console.log('3')

new Promise(function (resolve) {
  console.log('4')
  resolve()
}).then(function () {
  console.log('6')
})
複製代碼

上述的輸出結果爲:1,2,3,4,5,6

  1. 首先輸出1, 而後進入async1函數,輸出2
  2. await後面雖然是一個直接量,可是仍是先執行async函數外的同步代碼
  3. 輸出3, 進入promise輸出4,then回調進入微任務隊列
  4. 如今同步代碼執行完了, 回到async函數繼續執行輸出5
  5. 最後運行微任務輸出6
async function async1() {
  console.log('2')
  await async2()
  console.log('6')
}

async function async2() {
  console.log('3')
}

setTimeout(function () {
  console.log('8')
}, 0)

console.log('1')

async1()

new Promise(function (resolve) {
  console.log('4')
  resolve()
}).then(function () {
  console.log('7')
})

console.log('5')
複製代碼

上述的輸出結果爲:1,2,3,4,5,6,7,8

  1. 首先輸出同步代碼1, 而後進入async1方法輸出2
  2. 由於遇到await因此先進羣async2方法,後面的7處於等待狀態
  3. 在async2中輸出3, 如今跳出async函數先執行外面的同步代碼
  4. 輸出4,5.then回調進入微任務棧
  5. 如今宏任務執行完了,而後回到async1函數接着往下執行輸出6
  6. 執行微任務輸出7
  7. 進入下一輪事件循環輸出8
async function async1() {
  console.log('2')
  const data = await async2()
  console.log(data)
  console.log('8')
}

async function async2() {
  return new Promise(function (resolve) {
    console.log('3')
    resolve('await的結果')
  }).then(function (data) {
    console.log('6')
    return data
  })
}
console.log('1')

setTimeout(function () {
  console.log('9')
}, 0)

async1()

new Promise(function (resolve) {
  console.log('4')
  resolve()
}).then(function () {
  console.log('7')
})
console.log('5')
複製代碼

執行結果: 1-2-3-4-5-6-7-await的結果-8-9

  1. 函數async1和async2只是定義先無論它, 首先輸出1
  2. setTimeout做爲宏任務進入宏任務隊列等待下一輪事件循環
  3. 進入async1函數輸出2,await下面的代碼進入等待狀態。
  4. 進入async2函數輸出3,then回調進入微任務隊列
  5. 如今執行外面的同步代碼, 輸出4,5,then回調進入微任務隊列
  6. 按序執行微任務,輸出6,7. 如今回到async1函數,
  7. 輸出data, 也就是await關鍵字等到的內容, 接着輸出8
  8. 進行下一輪事件循環輸出9
setTimeout(function () {
  console.log('8')
}, 0)

async function async1() {
  console.log('1')
  const data = await async2()
  console.log('6')
  return data
}

async function async2() {
  return new Promise(resolve => {
    console.log('2')
    resolve('async2的結果')
  }).then(data => {
    console.log('4')
    return data
  })
}

async1().then(data => {
  console.log('7')
  console.log(data)
})

new Promise(function (resolve) {
  console.log('3')
  resolve()
}).then(function () {
  console.log('5')
})
複製代碼

完整輸出結果:1-2-3-4-5-6-7-async2的結果-8

  1. setTimeout做爲宏任務進入宏任務隊列等待下一輪事件循環
  2. 先執行async1函數, 輸出1,而後6進入等待狀態,如今執行async2
  3. 輸出2, then回調進入微任務隊列
  4. 接下來執行外面的同步代碼3, then回調進入微任務隊列
  5. 按序列執行微任務,輸出4,5. 下面會帶async1函數
  6. 輸出了4以後執行return data, await拿到了內容
  7. 繼續執行輸出6, 執行了後面的return data纔出發async1的then回調輸出7以及data
  8. 進行第二輪事件循環輸出8

面試題

  • 題目一

    setTimeout(function() {
        console.log('4')
    })
    
    new Promise(function(resolve) {
        console.log('1') // 同步任務
        resolve()
    }).then(function() {
        console.log('3')
    })
    console.log('2')
    複製代碼

    輸出結果: 1-2-3-4

  • 題目二

    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')
        })
    複製代碼

    輸出結果:1-7-6-8-2-4-3-5-9-11-10-12

  • 題目三

    setTimeout(function () {
      console.log('6')
    }, 0)
    console.log('1')
    async function async1() {
      console.log('2')
      await async2()
      console.log('5')
    }
    async function async2() {
      console.log('3')
    }
    async1()
    console.log('4')
    複製代碼

    輸出結果:1-2-3-4-5-6

  • 題目四

    console.log('1')
    async function async1() {
      console.log('2')
      await 'await的結果'
      console.log('5')
    }
    
    async1()
    console.log('3')
    
    new Promise(function (resolve) {
      console.log('4')
      resolve()
    }).then(function () {
      console.log('6')
    })
    複製代碼

    輸出結果:1-2-3-4-5-6

參考資源

相關文章
相關標籤/搜索