關於js執行機制,老早以前就一直想寫篇文章作個總結,由於和js執行順序的面試題碰到的特別多,每次碰到老是會去網上查,沒有系統地總結,搞得每次碰到都是似懂非懂的感受,這篇文章就係統的總結一下js執行機制。es6
你們都知道js最大的特色就是單線程執行,這就是爲何js簡單易學的一個重要緣由,不須要考慮複雜的同步問題,可是單線程也會有一個問題,全部的任務在執行的過程當中都必須等待前一個任務執行完成才能執行,這樣就會帶來一個效率的問題,爲了解決這個問題,js將任務分爲兩種:同步任務和異步任務,同步任務就是以前說後一個任務必須等待前一個任務執行完成才能執行,是在主線程上執行的,而異步任務不會直接進入主線程執行,而是進入任務隊列,只有在任務隊列通知異步任務能夠執行時,纔會被推入主線程執行。讓咱們來看一個更加直觀的流程圖:面試
說到異步任務,最多見就是setTimeout和setInterval兩兄弟了,setTimeout是延遲必定時間後執行,可是隻執行一次,setInterval是每隔必定的時間執行一次,會執行屢次,可是有時候咱們會發現設置必定的延遲時間後,回調函數的執行時間會比咱們設置的時間要晚,這是爲何呢?上面咱們說過,在任務執行的時候setTimeout這類異步任務的回調會被放到異步隊列中等待執行,當延遲時間結束時,若是主線程的任務已經執行完了,也就是處在空閒狀態時,就會將任務隊列的回調推到主線程執行,可是當主線程的任務尚未執行完成時,就只能繼續等待,來看一個例子:promise
let before = new Date() setTimeout(() => { console.log(new Date() - before) }, 1000) for (let i = 0; i < 300000; i++) { console.log('time delay') }
從上面的例子就能夠看到:當咱們執行完setTimeout以後,馬上執行20萬次的循環,從執行結果能夠看到,setTimeout回調函數中的時間遠高於設置1000ms,這就是由於時間到了,可是主線程的任務尚未執行完成致使。這種問題在setInterval設置倒計時的常常遇到,倒計時開始的時候設置的時間是從服務器拿到的系統時間很準確,可是若是後面不按期像服務期請求系統時間進行校準的話,你可能會發現倒計時的誤差愈來愈來大,這就是主線程執行的時間比設定的延遲時間長致使的。服務器
在js中,異步任務除了有setTimeout這類的異步任務,還有一類就是es6中很經常使用promise...then這類的異步任務,所以除了同步任務和異步任務,任務還能夠更加細分爲macrotask(宏任務)和microtask(微任務)
macrotask: 包括setTimeout、setInterval和執行棧
microtask: 包括Promise、process.nextTick
要想理解這兩個概念,直接從一道簡單的面試題入手,來看一個例子:異步
setTimeout(function() { console.log(1) }, 0); new Promise(function(resolve, reject) { console.log(2); resolve() }).then(function() { console.log(3) }); process.nextTick(function () { console.log(4) }) console.log(5)
思考一下上面例子的輸出結果,咱們來仔細分析一下執行過程:函數
最後用一張圖來總結一下:spa
這篇文章簡單介紹了js執行機制,但願看了以後,能夠對你們認識js的執行機制會有所幫助。
若是有錯誤或不嚴謹的地方,歡迎批評指正,若是喜歡,歡迎點贊收藏線程