JavaScript之JS單線程|事件循環|事件隊列|執行棧

本博文基於知乎"JavaScript做用域問題?"一問,而引發了對JavaScript事件循環和單線程等概念與實踐上的研究、深刻理解。javascript

1、概念
  0.關鍵詞:JavaScript單線程事件循環(event loop)事件隊列(event queue)執行棧(execution context stack)
  php

  1.JavaScript引擎屬於單線程做業所謂單線程,是指在JS引擎中負責解釋和執行JavaScript代碼的線程只有一個,也不妨叫它主線程。JavaScript引擎屬於單線程做業,意味着:在同一時間只能執行一個代碼塊,這些代碼塊的執行就阻塞了異步事件的處理。[From JavaScript忍者祕籍]
  JavaScript的單線程,與它的用途有關。做爲瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操做DOM。這決定了它只能是單線程,不然會帶來很複雜的同步問題。好比,假定JavaScript同時有兩個線程,一個線程在某個DOM節點上添加內容,另外一個線程刪除了這個節點,這時瀏覽器應該以哪一個線程爲準?
    1.1 單線程意味着,【全部任務】都須要排隊,前一個任務結束,纔會執行後一個任務。若是前一個任務耗時很長,後一個任務就不得不一直等着。
    1.2 若是排隊是由於計算量大,CPU忙不過來,倒也算了,可是不少時候CPU是閒着的,由於IO設備(輸入輸出設備)很慢(好比Ajax操做從網絡讀取數據),不得不等着結果出來,再往下執行。
    1.3 JavaScript語言的設計者意識到,這時主線程徹底能夠無論IO設備,掛起處於等待中的任務,先運行排在後面的任務。等到IO設備返回告終果,再回過頭,把掛起的任務繼續執行下去。
    1.4 因而,全部任務能夠分紅兩種,一種是同步任務(synchronous),另外一種是異步任務(asynchronous)。
    1.5 同步任務指的是,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;
    1.6 異步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行。
    1.7 具體來講,異步執行的運行機制以下。(同步執行也是如此,由於它能夠被視爲沒有異步任務的異步執行。)
      (1)全部同步任務都在主線程上執行,造成一個執行棧(execution context stack)。
      (2)主線程以外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。
      (3)一旦"執行棧"中的全部同步任務執行完畢,系統就會讀取"任務隊列",看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。
      (4)主線程不斷重複上面的第三步。
    1.8 只要主線程空了,就會去讀取"任務隊列",這就是JavaScript的運行機制。這個過程會不斷重複。
      [From 阮一峯老師:http://www.ruanyifeng.com/blog/2014/10/event-loop.html]

  2.事件循環(用於解決:異步問題/異步事件):在初期許多人會把異步理解成相似多線程的編程模式,其實他們中有着很大的差異,要徹底理解異步,就須要瞭解 JS 的運行核心——事件循環(event loop)。
    2.1 事件循環:【事件隊列】是一個存儲着待執行任務的隊列,其中的任務嚴格按照時間前後順序執行,排在隊頭的任務將會率先執行,而排在隊尾的任務會最後執行。事件隊列每次僅執行一個任務,在該任務執行完畢以後,再執行下一個任務。【執行棧】則是一個相似於函數調用棧的運行容器,當執行棧爲空時,JavaScript引擎便檢查事件隊列,若是不爲空的話,事件隊列便將第一個任務壓入中運行。
  [From http://www.php.cn/js-tutorial-369771.html]
    2.2 常見異步任務:定時器任務(setTimeout();setInterval();)、Ajax事件瀏覽器/用戶行爲事件(例如:瀏覽器加載(load)、鼠標單擊click、鼠標滑動/滑過/離開(mouseover、mouseout、mouseleave等)html

 

2、分析java

  因爲JavaScript是單線程做業,當一個異步事件發生時(好比:鼠標單擊、定時器觸發甚至是XMLHttpRequest的完成事件),它就會排隊,而且在線程空閒時才進行執行。且實際上,每一個瀏覽器的排隊機制是不一樣的。當咱們設置一個延遲函數的時候,當前腳本並不會阻塞,它只是會在瀏覽器的事件表中進行記錄,程序會繼續向下執行。當延遲的時間結束以後,事件表會將回調函數添加至事件隊列(task queue)中,事件隊列拿到了任務事後便將任務壓入執行棧(stack)當中,執行棧執行任務,執行console.log("after 1000 mills:",i);編程

for(var i = 0; i < 10; i++) {
    console.log("cur:",i);
	setTimeout(function() {
        console.log("after 1000 mills:",i); //當 console.log 被調用的時候,匿名函數保持對外部變量 i 的引用,此時for循環已經結束, i 的值被修改爲了 10. 
    }, 1000);
}

 

3、解決方案瀏覽器

  即時函數網絡

    格式:(function(){ //...statement })(); 多線程

for(var i = 0; i < 10; i++) {
	console.log("cur:",i),
	(function(i){
		setTimeout(function() {
       		console.log("after 1000 mills:",i); 
    	}, 1000);
	})(i);//經過即時函數(1.建立函數實例,2.執行該函數,3.銷燬該函數),將循環體異步事件壓入執行棧中,當即執行的特性,以維護好變量當前的值
}

 

 

4、引用文獻異步

  《JavaScript忍者祕籍》async

  JavaScript 運行機制詳解:再談Event Loop

  爲何會有異步? 什麼是事件隊列?

相關文章
相關標籤/搜索