[轉]JavaScript線程運行機制

 

 

  • 從開始接觸js時,咱們便知道js是單線程的。單線程,異步,同步,互調,阻塞等。在實際寫js的時候,咱們都會用到ajax,不論是原生的實現,仍是藉助jQuery等工具庫實現,咱們都知道,ajax能夠實現局部刷新,而且在請求處理時,任然能夠響應用戶的操做,好比點擊事件。不是說js是單線程嗎?這些都是怎麼實現的?
  • 在閱讀《深刻理解Bootrap的源碼》一書,在分析輪播組件(carousel.js)的源碼時,做者對一句代碼操做的註釋引發了個人興趣。
setTimeout(function(){
  that.$element.trigger('slid'); 
},0);//觸發slid事件,這裏使用setTimeout是爲了確保UI刷新線程不被阻塞。

 

後面我會一一解答這些疑惑。javascript

2.瀏覽器線程

  • js運做在瀏覽器中,是單線程的,即js代碼始終在一個線程上執行,這個線程稱爲js引擎線程。
  • 瀏覽器是多線程的,除了js引擎線程,它還有:
    1. UI渲染線程
    2. 瀏覽器事件觸發線程
    3. http請求線程
    4. EventLoop輪詢的處理線程
    5. ……..

這些線程的做用:html

  • UI線程用於渲染頁面
  • js線程用於執行js任務
  • 瀏覽器事件觸發線程用於控制交互,響應用戶
  • http線程用於處理請求,ajax是委託給瀏覽器新開一個http線程
  • EventLoop處理線程用於輪詢消息隊列

瀏覽器中的js任務:java

執行JavaScript代碼
對用戶的輸入(包含鼠標點擊、鍵盤輸入等等)作出反應
處理異步的網絡請求ajax

3.js單線程

  • 單線程的含義是js只能在一個線程上運行,也就說,js同時只能執行一個js任務,其它的任務則會排隊等待執行。
  • js是單線程的,並不表明js引擎線程只有一個。js引擎有多個線程,一個主線程,其它的後臺配合主線程。
  • 多線程之間會共享運行資源,瀏覽器端的js會操做dom,多個線程必然會帶來同步的問題,全部js核心選擇了單線程來避免處理這個麻煩。js能夠操做dom,影響渲染,因此js引擎線程和UI線程是互斥的。這也就解釋了js執行時會阻塞頁面的渲染。

4.消息隊列(任務隊列)

JavaScript運行時,除了一個運行線程,引擎還提供一個消息隊列,裏面是各類須要當前程序處理的消息。新的消息進入隊列的時候,會自動排在隊列的尾端。 編程

  • 消息和回調函數相互聯繫;

  單線程意味着js任務須要排隊,若是前一個任務出現大量的耗時操做,後面的任務得不到執行,任務的積累會致使頁面的「假死」。這也是js編程一直在強調須要迴避的「坑」。瀏覽器

5.js任務

任務分爲2種: markdown

  • 同步任務
  • 異步任務

它們的區別是: 本段中的線程指的是js引擎主線程網絡

  • 同步任務:在主線程排隊支持的任務,前一個任務執行完畢後,執行後一個任務,造成一個執行棧,線程執行時在內存造成的空間爲棧,進程造成堆結構,這是內存的結構。執行棧能夠實現函數的層層調用。注意不要理解成同步代碼進入棧中,按棧的出棧順序來執行。
  • 異步任務會被主線程掛起,不會進入主線程,而是進入消息隊列,並且必須指定回調函數,只有消息隊列通知主線程,而且執行棧爲空時,該消息對應的任務纔會進入執行棧得到執行的機會。

主線程執行的說明: 【js的運行機制】 多線程

(1)全部同步任務都在主線程上執行,造成一個執行棧。
(2)主線程以外,還存在一個」任務隊列」。只要異步任務有了運行結果,就在」任務隊列」之中放置一個事件。
(3)一旦」執行棧」中的全部同步任務執行完畢,系統就會讀取」任務隊列」,看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。
(4)主線程不斷重複上面的第三步。 dom

執行棧中的代碼(同步任務),老是在讀取」任務隊列」(異步任務)以前執行。

6.事件和回調函數

  • 消息隊列隊列(或者叫任務隊列)是一個事件的隊列,IO響應時,會往隊列中添加一個消息,此時說明相關的異步代碼到了執行的時機,能夠進入主線程的執行棧了。
  • 主線程讀取消息隊列,能夠讀取到對應的事件。
  • 消息隊列能夠響應IO事件,還有用戶產生的事件(好比點擊鼠標,頁面滾動),只要指定了回調函數,就會進入消息隊列,等待EventLoop輪詢線程處理,是否能夠進入主線程的執行棧。
  • 消息和回調函數相互聯繫的含義:主線程讀到消息,就會執行相應的回調函數;進入消息隊列的消息,必須對應相應的回調函數,不然這個消息會被丟棄不會進入消息隊列。
  • 消息隊列是一個先進先出的隊列結構,這就決定了它的執行順序,先產生的消息會被主線程先讀取,會不會執行則會先檢查一下執行時間,由於存在setTimeout等定時函數,這類事件產生的消息進入到消息隊列,被執行的時機取決與它在隊列中的位置和執行時間有關。【上文中使用setTimeout可以避免阻塞UI線程就是這個緣由】。

7.EventLoop

主線程從」任務隊列」中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)。

簡單說,瀏覽器的兩個線程:一個負責程序自己的運行,稱爲」主線程」;另外一個負責主線程與其餘進程(主要是各類I/O操做)的通訊,被稱爲」Event Loop線程」(能夠譯爲」消息線程」)。

因爲js是運行在單線程上的,全部瀏覽器單獨開啓一個線程來處理事件消息的輪詢,避免阻塞js的執行。

異步代碼的執行邏輯:
  每當遇到I/O的時候,主線程就讓EventLoop線程去通知相應的I/O程序,而後接着日後運行,因此不存在等待時間。等到I/O程序完成操做,EventLoop線程把消息添加到消息隊列,主線程就調用事先設定的回調函數,完成整個任務。

  • JavaIO中包括了網絡IO,咱們一般把http請求歸類爲網絡IO.
  • js的ajax是new XMLHttpRequest()對象實現的,瀏覽器會新開一個線程來處理http請求,這就是ajax可以實現局部刷新的同時,還能響應用戶交互的緣由。

這也說明了在處理IO時,瀏覽器一般會新開啓IO線程,這個屬於個人推測,並無查到對應的資料,由於IO涉及的普遍,這話也沒錯。

8.定時器

  前面也提到了定時器,定時器是會在進入消息隊列,這也就和異步代碼的執行邏輯同樣了。它在」消息隊列」的尾部添加一個消息,所以要等到同步任務和」消息隊列」現有的任務都處理完,纔會獲得執行的機會,還要看定時器設置的時間是否到了纔會執行。

9.關於異步代碼的說明.

  從上文咱們能夠得知:使用定時器和事件能夠實現異步,這是2種最爲明顯的實現異步的原理。對於異步的實現,阮一峯的博客說的很是清晰。

注:轉載自http://blog.csdn.net/w2765006513/article/details/53743051

相關文章
相關標籤/搜索