摘要:這種問題在設置倒計時的經常遇到,倒計時開始的時候設置的時間是從服務器拿到的系統時間很準確,但是如果后面不定期像服務期請求系統時間進行校準的話,你可能會發現倒計時的偏差越來越來大,這就是主線程執行的時間比設定的延遲時間長導致的。
關于js執行機制,老早之前就一直想寫篇文章做個總結,因為和js執行順序的面試題碰到的特別多,每次碰到總是會去網上查,沒有系統地總結,搞得每次碰到都是似懂非懂的感覺,這篇文章就系統的總結一下js執行機制。
任務隊列大家都知道js最大的特點就是單線程執行,這就是為什么js簡單易學的一個重要原因,不需要考慮復雜的同步問題,但是單線程也會有一個問題,所有的任務在執行的過程中都必須等待前一個任務執行完成才能執行,這樣就會帶來一個效率的問題,為了解決這個問題,js將任務分為兩種:同步任務和異步任務,同步任務就是之前說后一個任務必須等待前一個任務執行完成才能執行,是在主線程上執行的,而異步任務不會直接進入主線程執行,而是進入任務隊列,只有在任務隊列通知異步任務可以執行時,才會被推入主線程執行。讓我們來看一個更加直觀的流程圖:
setTimeout和setInterval說到異步任務,最常見就是setTimeout和setInterval兩兄弟了,setTimeout是延遲一定時間后執行,但是只執行一次,setInterval是每隔一定的時間執行一次,會執行多次,但是有時候我們會發現設置一定的延遲時間后,回調函數的執行時間會比我們設置的時間要晚,這是為什么呢?上面我們說過,在任務執行的時候setTimeout這類異步任務的回調會被放到異步隊列中等待執行,當延遲時間結束時,如果主線程的任務已經執行完了,也就是處在空閑狀態時,就會將任務隊列的回調推到主線程執行,但是當主線程的任務還沒有執行完成時,就只能繼續等待,來看一個例子:
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設置倒計時的經常遇到,倒計時開始的時候設置的時間是從服務器拿到的系統時間很準確,但是如果后面不定期像服務期請求系統時間進行校準的話,你可能會發現倒計時的偏差越來越來大,這就是主線程執行的時間比設定的延遲時間長導致的。
macrotask和microtask在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)
思考一下上面例子的輸出結果,我們來仔細分析一下執行過程:
第一輪:主線程開始執行,遇到setTimeout,將setTimeout的回調函數丟到宏任務隊列中,在往下執行new Promise立即執行,輸出2,then的回調函數丟到微任務隊列中,再繼續執行,遇到process.nextTick,同樣將回調函數扔到為任務隊列,再繼續執行,輸出5,當所有宏任務執行完成后看有沒有可以執行的微任務,發現有then函數和nextTick兩個微任務,先執行哪個呢?process.nextTick指定的異步任務總是發生在所有異步任務之前,因此先執行process.nextTick輸出4然后執行then函數輸出3,第一輪執行結束。
第二輪從宏任務隊列開始,發現setTimeout回調,輸出1執行完畢,因此結果是25431
最后用一張圖來總結一下:
總結這篇文章簡單介紹了js執行機制,希望看了之后,可以對大家認識js的執行機制會有所幫助。
如果有錯誤或不嚴謹的地方,歡迎批評指正,如果喜歡,歡迎點贊收藏
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100076.html
摘要:事件循環背景是一門單線程非阻塞的腳本語言,單線程意味著,代碼在執行的任何時候,都只有一個主線程來處理所有的任務。在意識到該問題之際,新特性中的可以讓成為一門多線程語言,但實際開發中使用存在著諸多限制。這個地方被稱為執行棧。 事件循環(Event Loop) 背景 JavaScript是一門單線程非阻塞的腳本語言,單線程意味著,JavaScript代碼在執行的任何時候,都只有一個主線程來...
摘要:以多線程的形式,允許單個任務分成不同的部分進行運行。提供協調機制,一方面防止進程之間和線程之間產生沖突,另一方面允許進程之間和線程之間共享資源。主線程會不斷的重復上訴過程。 眾所周知,js是單線程的,說到線程,我們首先來仔細辨析一下線程和進程的知識。 一、進程與線程 阮一峰老師的一篇文章寫的很好 cpu會給當前進程分配資源,進程是資源分配的最小單位,進程的資源會分配給線程使用,線程是C...
摘要:如果沒有其他異步任務要處理比如到期的定時器,會一直停留在這個階段,等待請求返回結果。執行的執行事件關閉請求的,例如事件循環的每一次循環都需要依次經過上述的階段。因此,才會早于執行。 showImg(https://segmentfault.com/img/bVbnY76); 概念 同步任務(Synchronous) 在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行后一個任務 ...
閱讀 1906·2021-11-22 14:44
閱讀 1672·2021-11-02 14:46
閱讀 3657·2021-10-13 09:40
閱讀 2600·2021-09-07 09:58
閱讀 1586·2021-09-03 10:28
閱讀 1658·2019-08-29 15:30
閱讀 976·2019-08-29 15:28
閱讀 1469·2019-08-26 12:20