熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> JSP教程 >> 正文

JavaScript能不能多線程?

2013-11-15 12:04:23  來源: JSP教程 

  JavaScript的setTimeout與setInterval是兩個很容易欺騙別人感情的方法因為我們開始常常以為調用了就會按既定的方式執行 我想不少人都深有同感 例如 [javascript]

  setTimeout( function(){ alert(‘你好!); } );

  setInterval( callbackFunction );

  setTimeout( function(){ alert(’你好!); } );

  setInterval( callbackFunction );

  認為setTimeout中的問候方法會立即被執行因為這並不是憑空而說而是JavaScript API文檔明確定義第二個參數意義為隔多少毫秒後回調方法就會被執行 這裡設成毫秒理所當然就立即被執行了

  同理對setInterval的callbackFunction方法每間隔毫秒就立即被執行深信不疑!

  但隨著JavaScript應用開發經驗不斷的增加和豐富有一天你發現了一段怪異的代碼而百思不得其解:

  [javascript]

  divonclick = function(){

  setTimeout( function(){documentgetElementById(inputField)focus();} );

  };

  divonclick = function(){

  setTimeout( function(){documentgetElementById(inputField)focus();} );

  };

  既然是毫秒後執行那麼還用setTimeout干什麼 此刻 堅定的信念已開始動搖

  直到最後某一天 你不小心寫了一段糟糕的代碼:

  [javascript]

  setTimeout( function(){ while(true){} } );

  setTimeout( function(){ alert(‘你好!); } );

  setInterval( callbackFunction );

  setTimeout( function(){ while(true){} } );

  setTimeout( function(){ alert(’你好!); } );

  setInterval( callbackFunction );

  第一行代碼進入了死循環但不久你就會發現第二第三行並不是預料中的事情alert問候未見出現callbacKFunction也杳無音訊!

  這時你徹底迷惘了這種情景是難以接受的因為改變長久以來既定的認知去接受新思想的過程是痛苦的但情事實擺在眼前對JavaScript真理的探求並不會因為痛苦而停止下面讓我們來展開JavaScript線程和定時器探索之旅!

  出現上面所有誤區的最主要一個原因是:潛意識中認為JavaScript引擎有多個線程在執行JavaScript的定時器回調函數是異步執行的

  而事實上的JavaScript使用了障眼法在多數時候騙過了我們的眼睛這裡背光得澄清一個事實:

  JavaScript引擎是單線程運行的浏覽器無論在什麼時候都只且只有一個線程在運行JavaScript程序

  JavaScript引擎用單線程運行也是有意義的單線程不必理會線程同步這些復雜的問題問題得到簡化

  那麼單線程的JavaScript引擎是怎麼配合浏覽器內核處理這些定時器和響應浏覽器事件的呢?

  下面結合浏覽器內核處理方式簡單說明

  浏覽器內核實現允許多個線程異步執行這些線程在內核制控下相互配合以保持同步假如某一浏覽器內核的實現至少有三個常駐線程:javascript引擎 線程界面渲染線程浏覽器事件觸發線程除些以外也有一些執行完就終止的線程如Http請求線程這些異步線程都會產生不同的異步事件下面通過一 個圖來闡明單線程的JavaScript引擎與另外那些線程是怎樣互動通信的雖然每個浏覽器內核實現細節不同但這其中的調用原理都是大同小異

  JavaScript的setTimeout與setInterval是兩個很容易欺騙別人感情的方法因為我們開始常常以為調用了就會按既定的方式執行 我想不少人都深有同感 例如

  由圖可看出浏覽器中的JavaScript引擎是基於事件驅動的這裡的事件可看作是浏覽器派給它的各種任務這些任務可以源自 JavaScript引擎當前執行的代碼塊如調用setTimeout添加一個任務也可來自浏覽器內核的其它線程如界面元素鼠標點擊事件定時觸發 器時間到達通知異步請求狀態變更通知等從代碼角度看來任務實體就是各種回調函數JavaScript引擎一直等待著任務隊列中任務的到來由於單線 程關系這些任務得進行排隊一個接著一個被引擎處理 上圖tttn表示不同的時間點tn下面對應的小方塊代表該時間點的任務假設現在是t時刻引擎運行在t對應的任務方塊代碼內在這個時間點內我們來描述一下浏覽器內核其它線程的狀態

  t時刻:

  GUI渲染線程:

  該線程負責渲染浏覽器界面HTML元素當界面需要重繪(Repaint)或由於某種操作引發回流(reflow)時該線程就會執行本文雖然重點解釋 JavaScript定時機制但這時有必要說說渲染線程因為該線程與JavaScript引擎線程是互斥的這容易理解因為 JavaScript腳本是可操縱DOM元素在修改這些元素屬性同時渲染界面那麼渲染線程前後獲得的元素數據就可能不一致了

  在JavaScript引擎運行腳本期間浏覽器渲染線程都是處於掛起狀態的也就是說被“凍結”了

  所以在腳本中執行對界面進行更新操作如添加結點刪除結點或改變結點的外觀等更新並不會立即體現出來這些操作將保存在一個隊列中待JavaScript引擎空閒時才有機會渲染出來

  GUI事件觸發線程:

  JavaScript腳本的執行不影響html元素事件的觸發在t時間段內首先是用戶點擊了一個鼠標鍵點擊被浏覽器事件觸發線程捕捉後形成一個鼠 標點擊事件由圖可知對於JavaScript引擎線程來說這事件是由其它線程異步傳到任務隊列尾的由於引擎正在處理t時的任務這個鼠標點擊事 件正在等待處理

  定時觸發線程:

  注意這裡的浏覽器模型定時計數器並不是由JavaScript引擎計數的因為JavaScript引擎是單線程的如果處於阻塞線程狀態就計不了時它必須依賴外部來計時並觸發定時所以隊列中的定時事件也是異步事件

  由圖可知在這t的時間段內繼鼠標點擊事件觸發後先前已設置的setTimeout定時也到達了此刻對JavaScript引擎來說定時觸發線程產生了一個異步定時事件並放到任務隊列中 該事件被排到點擊事件回調之後等待處理

  同理 還是在t時間段內接下來某個setInterval定時器也被添加了由於是間隔定時在t段內連續被觸發了兩次這兩個事件被排到隊尾等待處理

  可見假如時間段t非常長遠大於setInterval的定時間隔那麼定時觸發線程就會源源不斷的產生異步定時事件並放到任務隊列尾而不管它們是否 已被處理但一旦t和最先的定時事件前面的任務已處理完這些排列中的定時事件就依次不間斷的被執行這是因為對於JavaScript引擎來說在 處理隊列中的各任務處理方式都是一樣的只是處理的次序不同而已

  t過後也就是說當前處理的任務已返回JavaScript引擎會檢查任務隊列發現當前隊列非空就取出t下面對應的任務執行其它時間依此類推由此看來:

  如果隊列非空引擎就從隊列頭取出一個任務直到該任務處理完即返回後引擎接著運行下一個任務在任務沒返回前隊列中的其它任務是沒法被執行的

  相信您現在已經很清楚JavaScript是否可多線程也了解理解JavaScript定時器運行機制了下面我們來對一些案例進行分析:

  案例:setTimeout與setInterval

  [javascript]

  setTimeout(function(){

  /* 代碼塊 */

  setTimeout(argumentscallee );

  } );

  setInterval(function(){

  /*代碼塊 */

  } );

  setTimeout(function(){

  /* 代碼塊 */

  setTimeout(argumentscallee );

  } );

  setInterval(function(){

  /*代碼塊 */

  } );

  這兩段代碼看一起效果一樣其實非也第一段中回調函數內的setTimeout是JavaScript引擎執行後再設置新的setTimeout 定時 假定上一個回調處理完到下一個回調開始處理為一個時間間隔理論兩個setTimeout回調執行時間間隔>=ms第二段自 setInterval設置定時後定時觸發線程就會源源不斷的每隔十秒產生異步定時事件並放到任務隊列尾理論上兩個setInterval回調執行時 間間隔<=

  案例:ajax異步請求是否真的異步?

  很多同學朋友搞不清楚既然說JavaScript是單線程運行的那麼XMLHttpRequest在連接後是否真的異步?

  其實請求確實是異步的不過這請求是由浏覽器新開一個線程請求(參見上圖)當請求的狀態變更時如果先前已設置回調這異步線程就產生狀態變更事件放到 JavaScript引擎的處理隊列中等待處理當任務被處理時JavaScript引擎始終是單線程運行回調函數具體點即還是單線程運行 onreadystatechange所設置的函數


From:http://tw.wingwit.com/Article/program/Java/JSP/201311/20222.html
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.