javaScript單線程和異步機制

javaScript是單線程的可是它的背後有瀏覽器的其餘線程爲其服務,其異步也得靠其餘線程來監聽事件的響應,並將回調函數推入到任務隊列等待執行。單線程所作的就是執行棧中的同步任前端

1、JavaScript單線程
在瀏覽器的一個頁面中,該頁面的JS程序只有一個線程,故曰單線程。由於是單線程,因此程序的執行順序就是從上到下依次執行,同一時間內只能有一段代碼被執行。那爲何不用多線程,這樣不是更能充分利用CPU,提升效率麼?java

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

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

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

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

javaScript單線程和異步機制函數

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

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

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

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

4、定時器
setTimeout的做用是在間隔必定的時間後,將回調函數插入任務隊列中,等棧中的同步任務都執行完畢後,再執行。由於棧中的同步任務也會耗時,因此間隔的時間通常會大於等於指定的時間。

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

setTimeout(function() {
console.log("a")
}, 0)

for(let i=0; i<10000; i++) {}
console.log("b")

// 結果:b a
setTimeout(function() {
console.log("a")
}, 0)

for(let i=0; i<10000; i++) {}
console.log("b")

// 結果:b a
打印結果代表回調函數並無馬上執行,而是等待棧中的任務執行完畢後才執行的。棧中的任務執行多久,它就得等多久。

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

相關文章
相關標籤/搜索