JavaScript在瀏覽器環境中的非同步
JavaScript在瀏覽器環境中的非同步
參考:
https://blog.csdn.net/qq_26222859/article/details/77622222
https://www.cnblogs.com/aaron—blog/p/10903118.html
單執行緒與多執行緒
JavaScript 預設情況下是單執行緒執行的(除了用H5 Web Workers),但執行js指令碼的環境可以是多執行緒的
眾所周知JavaScript是單執行緒執行的指令碼語言,即: 程逐行執行語句,上面的語句沒有執行完會阻塞下面語句的執行,但在不同的環境下,卻可以實現非同步的操作(如瀏覽器環境下,node環境下)。
瀏覽器的多執行緒
在JavaScript引擎中負責解析和執行JavaScript程式碼的執行緒只有一個。但是除了這個主程式以外,還有其他很多輔助執行緒。那麼諸如onclick
回撥,setTimeout
,Ajax
這些都是怎麼實現的呢?即瀏覽器搞了幾個其他執行緒去輔助JavaScript執行緒的執行。
瀏覽器有很多執行緒,例如:
- GUI渲染執行緒 - 用於更新頁面
- JavaScript引擎執行緒 - 用於解析JavaScript程式碼
- 定時器觸發執行緒 - 瀏覽器定時計數器並不是 js引擎計數
- 瀏覽器事件執行緒 - 用於解析BOM渲染等工作
- http執行緒 - 主要負責資料請求
- EventLoop輪詢處理執行緒 - 事件被觸發時該執行緒會把事件新增到待處理佇列的隊尾
- 等等等
瀏覽器的核心是多執行緒的,它們在核心制控下相互配合以保持同步,一個瀏覽器至少實現三個常駐執行緒:javascript引擎執行緒,GUI渲染執行緒,瀏覽器事件觸發執行緒。
瀏覽器的非同步邏輯
下面這張圖可以說明瀏覽器的非同步邏輯
- 首先,js引擎解析js程式碼(稱為同步過程,即把整個頁面的js程式碼從頭到尾執行下來),當處理到與其他執行緒相關的程式碼,就會分發給其他執行緒(比如onclick, setTimeout,setInterval,ajax請求, 對DOM進行寫操作等等…此時的分發過程還是同步過程)
- 其他執行緒處理完之後(其他執行緒處理請求的過程就是非同步過程)會把需要js引擎執行的任務放入callback queue裡(也叫任務佇列、訊息佇列、事件佇列)。 這個過程中,js並不會阻塞當前執行緒,去等待其他執行緒執行完畢。
- 且其他執行緒執行完畢後放進callback queue裡的事件,卻需要等待js引擎執行完畢當前stack裡的任務,空閒下來,才會被執行。
總結起來,JS執行緒不會等待其他執行緒執行,同樣地其他執行緒也不會等待JS執行緒(各司其職),而其他執行緒返回給JS執行緒的任務自然就需要在callback queue裡等待(其他執行緒交給JS引擎的任務,當然要等JS自己的事情先幹完)。
js引擎與GUI引擎是互斥的
雖然不同執行緒之間是互相獨立的,但當GUI引擎需要更新介面的時候,卻會受到JS執行緒的阻塞。
瞭解過 H5 Web Workers
的朋友應該知道,在 H5 Web Workers
出現之後,JS也可以“開掛” 實現多執行緒執行,當然這不在本文的討論範疇內。這裡只提一點, H5 Web Workers
的一大用處就是讓js引擎中的耗時大的計算不影響介面的響應。
例如,如果js引擎需要處理一段耗時很長的程式碼:遞迴計算斐波那契數列的第2000項…呵呵, 由於執行緒阻塞,此時頁面是無響應的,比如:
在
<input>
標籤裡鍵入無響應,js實現的輪播圖不再播放等
所以此時可以把遞迴計算斐波那契數列的函式交給Workers
分執行緒。
- js引擎與GUI引擎是互斥的
這裡就引出了一個問題:js引擎與GUI引擎是互斥的,也就是說GUI引擎在渲染時會阻塞js引擎計算,反過來也一樣。原因很簡單,如果在GUI渲染的時候,js改變了dom,那頁面到底聽誰的?這就會造成渲染不同步。
這裡插句題外話,無論
H5 Web Workers
如何神通廣大,都無法改變一個事實: 要實現對DOM的操作,還是隻能在js主執行緒上進行,而不能分到workers分執行緒上。原因也是不同執行緒的具體語句之間執行順序是不一定的,要避免不同執行緒對DOM修改造成衝突。
借用一下別人的例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<table border=1>
<tr><td><button id='do'>Do long calc - bad status!</button></td>
<td><div id='status'>Not Calculating yet.</div></td>
</tr>
<tr><td><button id='do_ok'>Do long calc - good status!</button></td>
<td><div id='status_ok'>Not Calculating yet.</div></td>
</tr>
</table>
<script>
function long_running(status_div) {
var result = 0;
for (var i = 0; i < 1000; i++) {
for (var j = 0; j < 700; j++) {
for (var k = 0; k < 300; k++) {
result = result + i + j + k;
}
}
}
document.querySelector(status_div).innerHTML = 'calclation done' ;
}
document.querySelector('#do').onclick = function () {
document.querySelector('#status').innerHTML = 'calculating....';
long_running('#status');
};
document.querySelector('#do_ok').onclick = function () {
document.querySelector('#status_ok').innerHTML = 'calculating....';
window.setTimeout(function (){ long_running('#status_ok') }, 0);
};
</script>
</body>
</html>
我們希望能看到計算的每一個過程,我們在程式開始,計算,結束時,都執行了一個dom操作,插入了代表當前狀態的字串,Not Calculating yet.和calculating…和calclation done.計算中是一個耗時的3重for迴圈. 在沒有使用settimeout的時候,執行結果是由Not Calculating yet 直接跳到了calclation done.這顯然不是我們希望的.而造成這樣結果的原因正是js的事件迴圈單執行緒機制.dom操作是非同步的,for迴圈計算是同步的.非同步操作都會被延遲到同步計算之後執行.也就是程式碼的執行順序變了.calculating…和calclation done的dom操作都被放到事件佇列後面而且緊跟在一起,造成了丟幀.無法實時的反應.這個例子也告訴了我們,在需要實時反饋的操作,如渲染等,和其他相關同步的程式碼,要麼一起同步,要麼一起非同步才能保證程式碼的執行順序.在js中,就只能讓同步程式碼也非同步.即給for計算加上settimeout.
setTimeout(0): 手動非同步
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script>
alert(1);
setTimeout("alert(2)", 0);
alert(3);
</script>
</body>
</html>
上面這個程式碼的輸出順序是1->3->2,原因就是1和3是JS引擎中的同步過程,優先執行,而2是非同步過程,要等JS自己的同步過程執行完了,再被執行。
相關文章
- JavaScript在瀏覽器環境下的事件迴圈(Event Loop)JavaScript瀏覽器事件OOP
- 在nodejs環境裡使用瀏覽器環境下的document物件NodeJS瀏覽器物件
- 理解event loop(瀏覽器環境與nodejs環境)OOP瀏覽器NodeJS
- 使用基於 WebRTC 的 JavaScript API 在瀏覽器環境裡呼叫本機攝像頭WebJavaScriptAPI瀏覽器
- 從瀏覽器環境到JavaScript執行流程的一次簡單梳理瀏覽器JavaScript
- 徹底理解 JS Event Loop(瀏覽器環境)JSOOP瀏覽器
- 瀏覽器/nodeJS 中的事件環工作原理瀏覽器NodeJS事件
- 通過配置tomcat在瀏覽器訪問非專案中的檔案Tomcat瀏覽器
- 谷歌瀏覽器禁用JavaScript谷歌瀏覽器JavaScript
- JavaScript瀏覽器事件物件JavaScript瀏覽器事件物件
- JavaScript有同步任務和非同步任務,瀏覽器是怎麼處理的?JavaScript非同步瀏覽器
- JavaScript中的瀏覽器檢測和DOM基礎JavaScript瀏覽器
- 瀏覽器中的JavaScript:文件物件模型與 DOM 操作瀏覽器JavaScript物件模型
- 在 vscode.dev 中直接執行 Python !純瀏覽器環境,無後端!VSCodedevPython瀏覽器後端
- 瀏覽器的事件環機制瀏覽器事件
- Rtsp轉Flv在瀏覽器中播放瀏覽器
- 在瀏覽器中執行vscode -DEV瀏覽器VSCodedev
- 淺談瀏覽器執行環境下的event loop機制瀏覽器OOP
- Node.js的勁敵來了:Deno是用於在Web瀏覽器之外執行JavaScript和TypeScript的執行環境Node.jsWeb瀏覽器JavaScriptTypeScript
- JS在瀏覽器中的執行機制JS瀏覽器
- 如何在瀏覽器中測試JavaScript程式碼?瀏覽器JavaScript
- WebVM:無需後端伺服器直接在瀏覽器中實現的無伺服器環境Web後端伺服器瀏覽器
- Linux伺服器部署Web版VSCode,在window下使用瀏覽器在linux環境下編寫程式碼Linux伺服器WebVSCode瀏覽器
- face-api.js:一個在瀏覽器中進行人臉識別的 JavaScript 介面APIJS瀏覽器JavaScript
- 原生ES-Module在瀏覽器中的嘗試瀏覽器
- js 在瀏覽器中的event loop事件佇列JS瀏覽器OOP事件佇列
- javascript事件迴圈(瀏覽器/node)JavaScript事件瀏覽器
- JavaScript 複習之瀏覽器模型JavaScript瀏覽器模型
- 翻譯 | 擺脫瀏覽器限制的JavaScript瀏覽器JavaScript
- 瀏覽器 Javascript 的 EventLoop 動態圖析瀏覽器JavaScriptOOP
- 跨瀏覽器的JavaScript效能檢測工具瀏覽器JavaScript
- 【譯】瀏覽器如何工作:在現代web瀏覽器場景的之下瀏覽器Web
- 瀏覽器/nodeJS中的EventLoop瀏覽器NodeJSOOP
- 瀏覽器中的Event Loop瀏覽器OOP
- 360瀏覽器在電腦中開啟網頁無痕瀏覽的設定方法瀏覽器網頁
- 在 Windows 11 中解除安裝 Edge 瀏覽器Windows瀏覽器
- Ooui:在瀏覽器中執行.NET應用UI瀏覽器
- emoji在瀏覽器中是如何傳遞給伺服器的瀏覽器伺服器