沐鳴平台網站_幾道面試題來看JavaScript執行機制

前面的話

根據 JavaScript 的運行環境,鎖定它為單線程,任務需要排隊執行,如果網站資源比較大,這樣會導致瀏覽器加載會很慢,但實際上並沒有,大家肯定立刻想到了同步和異步。

所謂的同步和異步也是在排隊,只是排隊的地方不同。

同步和異步

同步任務進入主線程排隊,異步任務進入事件隊列中排隊

同步任務和異步任務進入到不同的隊列中,也就是上面講的在不同地方排隊。

同步任務進入主線程,異步任務進入事件隊列,主線程任務執行完畢,事件隊列中有等待執行的任務進入主線程執行,直到事件隊列中任務全部執行完畢。

開胃菜

console.log('a')

setTimeout(function(){
    console.log('b')
}, 200)

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

console.log('d')

結果:a d c b

從上到下,該進入主線程的進入主線程,該進入事件隊列的進入事件隊列。

那麼主線程中存在 console.log(‘a’) 和 console.log(‘d’),定時器 setTimeout 延遲一段時間執行,顧名思義異步任務進入事件隊列中,等待主線程任務執行完畢,再進入主線程執行。

定時器的延遲時間為 0 並不是立刻執行,只是代表相比於其他定時器更早的進入主線程中執行。

加一盤

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i)
    }, 1000)
}

結果:十個10

每次 for 循環遇到 setTimeout 將其放入事件隊列中等待執行,直到全部循環結束,i 作為全局變量當循環結束后 i = 10 ,再來執行 setTimeout 時 i 的值已經為 10 , 結果為十個10。

將 var 改為 let ,變量作用域不同,let 作用在當前循環中,所以進入事件隊列的定時器每次的 i 不同,最後打印結果會是 0 1 2…9。

宏任務 微任務

除了經常說的同步任務和異步任務之外,更可分為宏任務,微任務

主要宏任務:整段腳本script setTimeout setTimeout…
主要微任務:promise.then…

執行流程:

  1. 整段腳本script作為宏任務開始執行
  2. 遇到微任務將其推入微任務隊列,宏任務推入宏任務隊列
  3. 宏任務執行完畢,檢查有沒有可執行的微任務
  4. 發現有可執行的微任務,將所有微任務執行完畢
  5. 開始新的宏任務,反覆如此直到所有任務執行完畢

來一盤Promise

const p = new Promise(resolve => {
    console.log('a')
    resolve()
    console.log('b')
})

p.then(() => {
    console.log('c')
})

console.log('d')

結果:a b d c

  1. 整段script進入宏任務隊列開始執行,
  2. promise 創建立即執行,打印 a b,
  3. 遇到 promise.then 進入微任務隊列,
  4. 遇到 console.log(‘d’) 打印 d,
  5. 整段代碼作為宏任務執行完畢,有可執行的微任務,開始執行微任務,打印 c 。
setTimeout(function(){
    console.log('setTimeout')
}, 0)

const p = new Promise(resolve => {
    console.log('a')
    resolve()
    console.log('b')
})

p.then(() => {
    console.log('c')
})

console.log('d')

結果:a b d c setTimeout

  1. setTimeout 進入宏任務隊列,
  2. promise 創建立即執行,打印 a b,
  3. 遇到 promise.then 進入微任務隊列,
  4. 遇到 console.log(‘d’) 打印 d,
  5. 有可執行的微任務,打印 c,
  6. 微任務執行完畢,開始執行新的宏任務,setTimeout 開始執行,打印 setTimeout
setTimeout(function(){
    console.log('setTimeout')
}, 0)

const p = new Promise(resolve => {
    console.log('a')
    resolve()
    console.log('b')
})

p.then(() => {
    console.log('c')
    setTimeout(function(){
        console.log('then中的setTimeout')
    }, 0)
})

console.log('d')

結果:a b d c setTimeout then中的setTimeout

  1. 同上
  2. 執行微任務打印 c,遇到 setTimeout 將其推入宏任務隊列中
  3. 定時器延遲時間相同,開始按照順序執行宏任務,分別打印 setTimeout then中的setTimeout

再加點定時器

console.log('a');

new Promise(resolve => {
    console.log('b')
    resolve()
}).then(() => {
    console.log('c')
    setTimeout(() => {
      console.log('d')
    }, 0)
})

setTimeout(() => {
    console.log('e')
    new Promise(resolve => {
        console.log('f')
        resolve()
    }).then(() => {
        console.log('g')
    })
}, 100)

setTimeout(() => {
    console.log('h')
    new Promise(resolve => {
        resolve()
    }).then(() => {
        console.log('i')
    })
    console.log('j')
}, 0)

結果:a b c h j i d e f g

  1. 打印 a
  2. promise 立即執行,打印 b
  3. promise.then 推入微任務隊列
  4. setTimeout 推入宏任務隊列
  5. 整段代碼執行完畢,開始執行微任務,打印 c ,遇到 setTimeout 推入宏任務隊列排隊等待執行
  6. 沒有可執行的微任務開始執行宏任務,定時器按照延遲時間排隊執行
  7. 打印 h j ,promise.then 推入微任務隊列
  8. 有可執行的微任務,打印 i ,繼續執行宏任務,打印 d
  9. 執行延遲為100的宏任務,打印 e f,執行微任務打印 g,所有任務執行完畢

簡單測試

console.log('start')

a().then(() => {
  console.log('a_then')
})

console.log('end')

function a() {
  console.log('a_function')
  return b().then((res) => {
    console.log('res', res)
    console.log('b_then')
    return Promise.resolve('a方法的返回值')
  })
}

function b() {
  console.log('b_function')
  return Promise.resolve('返回值')
}

結果:start a_function b_function end res 返回值 b_then a_then

根據上面例子的流程講解來思考這個,加深理解

總結

  • JavaScript 單線程,任務需要排隊執行
  • 同步任務進入主線程排隊,異步任務進入事件隊列排隊等待被推入主線程執行
  • 定時器的延遲時間為 0 並不是立刻執行,只是代表相比於其他定時器更早的被執行
  • 以宏任務和微任務進一步理解js執行機制
  • 整段代碼作為宏任務開始執行,執行過程中宏任務和微任務進入相應的隊列中
  • 整段代碼執行結束,看微任務隊列中是否有任務等待執行,如果有則執行所有的微任務,直到微任務隊列中的任務執行完畢,如果沒有則繼續執行新的宏任務
  • 執行新的宏任務,凡是在執行宏任務過程中遇到微任務都將其推入微任務隊列中執行
  • 反覆如此直到所有任務全部執行完畢

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/8130