JavaScript單線程和異步機制


隨着對JavaScript學習的深入和實踐經驗的積累,一些原理和底層的東西也開始逐漸了解。早先也看過一些關於js單線程和事件循環的文章,不過當時看的似懂非懂,只留了一個大概的印象:瀏覽器中的js程序時是單線程的。嗯,就這么點印象。當時也有些疑問:既然是單線程的,那異步調用是怎么實現的?計時器是靠誰來計時的,這單線程總不能一邊執行程序一邊計時吧?那些耗時的I/O操作為啥沒把線程阻塞,不是說好的單線程么?相信很多不了解JavaScript單線程的同學也有過類似的疑問。

今天看了不少相關的資料,就詳細地總結一下,希望能幫到大家,同時自己也梳理一下知識點,理解是一回事,能寫下來是另一回事。好了,開始正文。

一、JavaScript單線程

在瀏覽器的一個頁面中,該頁面的JS程序只有一個線程,故曰單線程。因為是單線程,所以程序的執行順序就是從上到下依次執行,同一時間內只能有一段代碼被執行。那為什么不用多線程,這樣不是更能充分利用CPU,提高效率么?

早期的網頁內容非常簡單,單線程足以應付,所以在設計之初,我估計設計者就沒考慮使用多線程。另外,JavaScript主要用來處理用戶與頁面產生的交互,以及操作DOM;如果以多線程的方式來操作DOM,一個線程要求刪除該DOM,另一個要求修改DOM樣式,那么瀏覽器該聽誰的?這大大增加了程序設計的復雜度,本來前端都不好學,不是么~~~簡簡單單多好。

雖然JavaScript是單線程的,可是瀏覽器內部不是單線程的。你的一些I/O操作、定時器的計時和事件監聽(click, keydown…)等都是由瀏覽器提供的其他線程來完成的。

如果想利用多線程處理一些耗時較長的任務,可以使用HTML5提出的Web Worker。

二、任務隊列和事件循環

提到異步機制,不得不說到任務隊列和事件循環,這里理解JS異步機制的要點。

首先理解瀏覽器的並發模型,來看下面這個圖:

左邊的棧存儲的是同步任務,所謂同步的任務就是那些能立即執行、不耗時的任務,如變量和函數的初始化、事件的綁定等等那些不需要回調函數的操作都可歸為這一類。右邊的堆用來存儲聲明的變量、對象。下面的隊列就是任務隊列,一旦某個異步任務有了響應就會被推入隊列中。如用戶的點擊事件、瀏覽器收到服務的響應和后面提到的setTimeout插入的事件。每個異步任務都和一個回調函數相關聯。

一個js程序的單線程用來執行棧中的同步任務,當所有同步任務執行完畢后,棧被清空,然后讀取任務隊列中的一個待處理任務,並把相關回調函數壓入棧中,單線程開始執行新的同步任務,執行完畢。

單線程從任務隊列中讀取任務是不斷循環的,每次棧被清空后,都會在任務隊列中讀取新的任務,如果沒有新的任務,就會等待,直到有新的任務,這就叫任務循環。因為每個任務都由一個事件所觸發,所以也叫事件循環

三、異步機制

有了上面兩節做鋪墊,理解異步機制就容易多了。拿ajax來說,當頁面的單線程執行xhr.send()之后,對於頁面來說發送任務已經完成了。怎么發送,那是瀏覽器的事,和單線程無關;什么時候響應,這事說不准。為了及時地得到響應的內容,在單線程中注冊相應的事件就好xhr.onreadystatechange = fn() {...}。注冊之后,瀏覽器會在內部的其他線程中自動地幫我們監聽該事件。直到該事件被觸發,瀏覽器會在任務隊列中添加一個任務等待該單線程執行。

四、定時器

setTimeout的作用是在間隔一定的時間后,將回調函數插入任務隊列中,等棧中的同步任務都執行完畢后,再執行。因為棧中的同步任務也會耗時,所以間隔的時間一般會大於等於指定的時間。

setTimeout(fn, 0)的意思是,將回調函數fn立刻插入任務隊列,等待執行,而不是立即執行。看一個例子:

1
2
3
4
5
6
7
8
setTimeout( function() {
console.log("a")
}, 0)
for(let i=0; i<10000; i++) {}
console.log("b")
// 結果:b a

打印結果表明回調函數並沒有立刻執行,而是等待棧中的任務執行完畢后才執行的。棧中的任務執行多久,它就得等多久。

五、總結

JavaScript單線程和其異步機制就如上所述。所謂的單線程並不孤單,它的背后有瀏覽器的其他線程為其服務,其異步也得靠其他線程來監聽事件的響應,並將回調函數推入到任務隊列等待執行。單線程所做的就是執行棧中的同步任務,執行完畢后,再從任務隊列中取出一個事件(沒有事件的話,就等待事件),然后開始執行棧中相關的同步任務,不斷的這樣循環。

參考資料

來源:http://ylcui.top/2016/08/14/JavaScript%E5%8D%95%E7%BA%BF%E7%A8%8B%E4%B8%8E%E4%BA%8B%E4%BB%B6%E5%BE%AA%E7%8E%AF/?utm_source=tuicool&utm_medium=referral


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM