基本概念html
一、js的執行過程是單線程的模式,也就是同步進行,只有前面的代碼執行完了纔會往下面執行ajax
二、可是執行js代碼也只是瀏覽器的線程之一所負責的事情,這個線程被稱爲js引擎,瀏覽器還具備其餘線程:界面渲染線程(UI)、瀏覽器事件觸發線程(控制交互,響應用戶)、http請求線程(處理請求,而ajax發送請求則會委託瀏覽器新開一個http線程)、EventLoop輪詢線程(負責輪詢消息隊列)編程
三、瀏覽器中js代碼的做用:執行JavaScript代碼 、對用戶的輸入(包含鼠標點擊、鍵盤輸入等等)作出反應 、處理異步的網絡請求瀏覽器
js單線程網絡
一、單線程的含義是js只能在一個線程上運行,也就說,js同時只能執行一個js任務,其它的任務則會排隊等待執行。多線程
二、js是單線程的,並不表明js引擎線程只有一個。js引擎有多個線程,一個主線程,其它的後臺配合主線程。閉包
三、多線程之間會共享運行資源,瀏覽器端的js會操做dom,多個線程必然會帶來同步的問題,全部js核心選擇了單線程來避免處理這個麻煩。js能夠操做dom,影響渲染,因此js引擎線程和UI線程是互斥的。這也就解釋了js執行時會阻塞頁面的渲染dom
js消息隊列異步
一、JavaScript運行時,除了一個運行線程,引擎還提供一個消息隊列,裏面是各類須要當前程序處理的消息。新的消息進入隊列的時候,會自動排在隊列的尾端函數
二、單線程意味着js任務須要排隊,若是前一個任務出現大量的耗時操做,後面的任務得不到執行,任務的積累會致使頁面的「假死」。這也是js編程一直在強調須要迴避的「坑」
js執行任務方式
一、首先js任務分兩種:同步任務、異步任務
二、同步任務:在主線程排隊支持的任務,前一個任務執行完畢後,執行後一個任務,造成一個執行棧,線程執行時在內存造成的空間爲棧,進程造成堆結構,這是內存的結構。執行棧能夠實現函數的層層調用。注意不要理解成同步代碼進入棧中,按棧的出棧順序來執行。
三、異步任務:會被主線程掛起,不會進入主線程,而是進入消息隊列,並且必須指定回調函數,只有消息隊列通知主線程,而且執行棧爲空時,該消息對應的任務纔會進入執行棧得到執行的機會。
四、主線程說明:
(1)全部同步任務都在主線程上執行,造成一個執行棧。
(2)主線程以外,還存在一個」任務隊列」(消息隊列)。只要異步任務有了運行結果,就在」任務隊列」之中放置一個事件。
(3)一旦」執行棧」中的全部同步任務執行完畢,系統就會讀取」任務隊列」(消息隊列),看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。
(4)主線程不斷重複上面的第三步。
五、消息隊列說明:
(1)消息隊列隊列(或者叫任務隊列)是一個事件的隊列,IO響應時(鼠標點擊等輸入輸出設備的操做),會往隊列中添加一個消息,此時說明相關的異步代碼到了執行的時機,能夠進入主線程的執行棧了。
(2)主線程讀取消息隊列,能夠讀取到對應的事件。
(3)消息隊列能夠響應IO事件,還有用戶產生的事件(好比點擊鼠標,頁面滾動),只要指定了回調函數,就會進入消息隊列,等待EventLoop輪詢線程處理,是否能夠進入主線程的執行棧。
(4)消息和回調函數相互聯繫的含義:主線程讀到消息,就會執行相應的回調函數;進入消息隊列的消息,必須對應相應的回調函數,不然這個消息會被丟棄不會進入消息隊列。
(5)消息隊列是一個先進先出的隊列結構,這就決定了它的執行順序,先產生的消息會被主線程先讀取,會不會執行則會先檢查一下執行時間,由於存在setTimeout等定時函數,這類事件產生的消息進入到消息隊列,被執行的時機取決與它在隊列中的位置和執行時間有關。【使用setTimeout可以避免阻塞UI線程就是這個緣由】。
五、須要注意的是:執行棧中的代碼(同步任務),老是在讀取」任務隊列」(異步任務)以前執行。
EventLoop
一、主線程從」任務隊列」中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)。
二、簡單說,瀏覽器的兩個線程:一個負責程序自己的運行,稱爲」主線程」;另外一個負責主線程與其餘進程(主要是各類I/O操做)的通訊,被稱爲」Event Loop線程」(能夠譯爲」消息線程」)。
三、因爲js是運行在單線程上的,全部瀏覽器單獨開啓一個線程來處理事件消息的輪詢,避免阻塞js的執行。
異步代碼執行邏輯
一、每當遇到I/O的時候,主線程就讓EventLoop線程去通知相應的I/O程序,而後接着日後運行,因此不存在等待時間。等到I/O程序完成操做,EventLoop線程把消息添加到消息隊列,主線程就調用事先設定的回調函數,完成整個任務。
二、js的ajax是new XMLHttpRequest()對象實現的,瀏覽器會新開一個線程來處理http請求,這就是ajax可以實現局部刷新的同時,還能響應用戶交互的緣由。
定時器
一、前面也提到了定時器,定時器是會在進入消息隊列,這也就和異步代碼的執行邏輯同樣了。它在」消息隊列」的尾部添加一個消息,所以要等到同步任務和」消息隊列」現有的任務都處理完,纔會獲得執行的機會,還要看定時器設置的時間是否到了纔會執行。
<script> for(var i = 0 ; i < 10; i++){ setTimeout(function(){ console.log(i);//打印10次10 },0); } </script>
因此,只有等到主線程的任務執行完以後,setTimeout中的事件纔會被執行,雖然時間間隔是0秒,可是必須等主線程任務完成,因此最後打印的都是10
二、能夠用閉包來解決問題,每次執行for循環的時候,把函數做爲返回值給setTimeout做爲參數,函數裏面存着從for循環裏面拿到的每個值,下面代碼會返回多個函數,每一個函數的j值都不同
<script> for(var i = 0; i< 3; i++){ function foo(j){ // var j; // j = 實參 //j = i return function(){ console.log(j); }; } //0 var f = foo(i); setTimeout(f, 0); } </script>
參考:
http://blog.csdn.net/w2765006513/article/details/53743051
https://www.cnblogs.com/haodawang/articles/5850822.html