摘要:事件循環(huán)當主線程中的任務執(zhí)行完畢后,會從任務隊列中獲取任務一個個的放在棧中執(zhí)行去執(zhí)行,這個過程是循環(huán)不斷的,所以整個的這種運行機制又稱為事件循環(huán)。
寫在前面
說起javascript(以下簡稱js)這門語言,相信大家已經非常熟悉了,不管是前端開發(fā)還是后端開發(fā)幾乎無時無刻都要跟它打交道。雖說開發(fā)者每天幾乎都要操作js,但是你真的確定你掌握了js的運行機制嗎!下面我們就來聊聊這話題。
JavaScript運行機制圖解上圖我們可以分為兩部分:瀏覽器中的JS引擎和運行環(huán)境Runtime,那它們的區(qū)別是什么?
JS引擎:編譯并執(zhí)行代碼的地方。
如上圖中可以看出JS引擎分為兩大核心部分:棧和堆
棧(Stack):js代碼的執(zhí)行都要壓到此棧中執(zhí)行。
堆:存放對象、數(shù)組的地方,js垃圾回收就是檢查這里。
Runtime:瀏覽器的運行環(huán)境,它提供了一些對外接口供JS調用,如網絡請求接口。
JavaScript引擎是單線程的JS引擎是單線程的,也就是說在一個時間段內,事情只能一件一件的按先后順序去做,第一件事沒做完就不能第二件事。那么在js引擎中負責解釋和執(zhí)行js代碼的線程只有一個,我們可以稱之為主線程。
當然瀏覽器的運行環(huán)境Runtime還提供一些其他的線程,如定時器線程、ajax線程、事件線程、網絡請求和UI渲染的線程,為了和js主線程分開,我們這里都統(tǒng)稱它們?yōu)?b>工作線程。
由于瀏覽器是多線程的,所以工作線程和js主線程都可以執(zhí)行任務,線程間互不干擾。
JavaScript同步(異步)任務在JavaScript任務可以分為兩種:
同步任務:在主線程上排隊執(zhí)行的任務,只有前一個任務執(zhí)行完畢,才能執(zhí)行后一個任務,若前一個任務耗費很長時間,則后面的任務會一直處于等待狀態(tài),即阻塞狀態(tài)。
異步任務:在棧執(zhí)行代碼的過程中,如遇到異步函數(shù),如setTimeout、異步Ajax、事件處理程序,會將這些異步代碼交給瀏覽器的工作線程來處理,我們把這些任務稱之為異步任務。異步任務是不進入主線程,而是進入任務隊列(queue task)。
什么異步函數(shù)?
異步函數(shù)通常是由發(fā)起函數(shù)和回調函數(shù)構成的。如:
A(callback)
函數(shù)A就是發(fā)起函數(shù)
callback就是回調函數(shù)
? 它們都是在主線程調用的,其中發(fā)起函數(shù)用來發(fā)起異步過程,回調函數(shù)用來處理結果。 如:`setTimeout(callback,1000)` setTimeout就是發(fā)起函數(shù)、callback就是回調函數(shù)。 如:異步的Ajax
var xhr = new new XMLHttpRequest(); xhr.onreadystatechange = callback; //callback為回調函數(shù) xhr.open("get",url,true); xhr.send(null); // send為發(fā)起函數(shù)
可以看出發(fā)起函數(shù)和回調函數(shù)也可以是分離的。
?
既然同步任務是在主線程中執(zhí)行的,那么異步任務何時執(zhí)行?
答:是這樣的,一旦棧中同步任務執(zhí)行完畢后,系統(tǒng)就會通過事件循環(huán)機制讀取任務隊列中的任務一個個移到棧中去執(zhí)行。
?
事件循環(huán)當主線程中的任務執(zhí)行完畢后,會從任務隊列中獲取任務一個個的放在棧中執(zhí)行去執(zhí)行,這個過程是循環(huán)不斷的,所以整個的這種運行機制又稱為事件循環(huán)。
棧在js中,代碼最終都是在棧中執(zhí)行的,棧結構的特點是:先進后出,后進先出。
我們來看下面代碼的運行結果:
function bar(){ console.log(1); foo(); } function foo(){ par(); console.log(3); } function par(){ setTimeout(function(){ console.log(2); },0); } bar();
運行的最終結果是:132。 為什么結果不是123呢?
下我們來分析下代碼運行時入棧和出棧的過程。
首先當調用函數(shù)bar()時,此函數(shù)就會先入棧,其內部的console.log(1)也會隨之入棧執(zhí)行。
執(zhí)行完console.log(1)后,就要出棧,于是控制臺先打印出結果1,只剩下bar()在棧中。接著再執(zhí)行函數(shù)bar內部的函數(shù)foo,于是函數(shù)foo也開心的入棧了。
執(zhí)行函數(shù)foo的內部代碼,調用函數(shù)par(),于是函數(shù)par()也要跟著入棧。
由于函數(shù)par()內部執(zhí)行遇到了異步函數(shù)setTimeout,異步函數(shù)則會由瀏覽器的Runtime運行環(huán)境的工作線程來處理,等定時器設置的時間到達就會被放到任務隊列中,此時棧的同步任務繼續(xù)執(zhí)行。
接著在執(zhí)行par函數(shù)中的console.log(3),控制臺打印結果為3 ,此時棧的代碼執(zhí)行完畢后,會按照棧的特點進行
先進后出,后進先出順序進行出棧。出棧順序:先函數(shù)par()-->后函數(shù)foo()-->最后函數(shù)bar。
最后只剩下異步任務,由主線程去獲取任務隊列中的任務放在棧中去執(zhí)行。也可以認為棧中的同步代碼執(zhí)行總是在讀取異步任務之前執(zhí)行。
最后執(zhí)行setTimeout中的回調函數(shù):結果控制臺輸出為2。
setTimeout(function(){ console.log(2); },0);
所以代碼的最終運行結果為132。
小結js引擎是單線程執(zhí)行js代碼,同步任務在棧中按順序執(zhí)行,如果某一個同步任務沒有執(zhí)行完畢,則后面的代碼將會處于阻塞等待狀態(tài)
棧中若執(zhí)行遇到了異步任務(如定時器、異步Ajax、事件),會將此異步任務通過瀏覽器對應的工作線程來處理。
工作線程中的所有異步任務均會按照設定的時間進行等待,時間一到會被加入任務隊列。如果是異步ajax,則等待其返回結果后在加入到任務隊列
當棧中為空時,會通過事件循環(huán)來一個個獲取任務隊列中的任務放到棧中進行逐個運行。即棧中的同步任務總是在讀取異步任務之前執(zhí)行
定時器設置的時間不一定按照設定的時間進行執(zhí)行,這得取決于棧中同步任務耗費的時間。因為棧中執(zhí)行的同步任務如果耗費很長時間,則會影響到異步任務回調函數(shù)的執(zhí)行。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/94222.html
摘要:此事件隊列的美妙之處在于它只是函數(shù)等待被調用和移動到調用棧的一個臨時存放區(qū)域。在事件循環(huán)不斷監(jiān)視調用棧是否為空現(xiàn)在確實是空的時候調用創(chuàng)建一個新的調用棧來執(zhí)行代碼。在執(zhí)行完之后進入了一個新的狀態(tài)這個狀態(tài)調用棧為空事件記錄表為空事件隊列也為空。 這篇文章是對個人認為講解 JavaScript 事件循環(huán)比較清楚的一篇英文文章的簡單翻譯,原文地址是http://altitudelabs.com...
摘要:主線程不斷重復上面的三步,此過程也就是常說的事件循環(huán)。所以主線程代碼執(zhí)行時間過長,會阻塞事件循環(huán)的執(zhí)行。參考資料這一次,徹底弄懂執(zhí)行機制任務隊列的順序機制事件循環(huán)搞懂異步事件輪詢與中的事件循環(huán) 1. 說明 讀過本文章后,您能知道: JavaScript代碼在瀏覽器中的執(zhí)行機制和事件循環(huán) 面試中經常遇到的代碼輸出順序問題 首先通過一段代碼來驗證你是否了解代碼輸出順序,如果你不知道輸出...
摘要:而事件循環(huán)是主線程中執(zhí)行棧里的代碼執(zhí)行完畢之后,才開始執(zhí)行的。由此產生的異步事件執(zhí)行會作為任務隊列掛在當前循環(huán)的末尾執(zhí)行。在下,觀察者基于監(jiān)聽事件的完成情況在下基于多線程創(chuàng)建。 主要問題: 1、JS引擎是單線程,如何完成事件循環(huán)的? 2、定時器函數(shù)為什么計時不準確? 3、回調與異步,有什么聯(lián)系和不同? 4、ES6的事件循環(huán)有什么變化?Node中呢? 5、異步控制有什么難點?有什么解決方...
摘要:概述本篇主要介紹的運行機制單線程事件循環(huán)結論先在中利用運行至完成和非阻塞完成單線程下異步任務的處理就是先處理主模塊主線程上的同步任務再處理異步任務異步任務使用事件循環(huán)機制完成調度涉及的內容有單線程事件循環(huán)同步執(zhí)行異步執(zhí)行定時器的事件循環(huán)開始 1.概述 本篇主要介紹JavaScript的運行機制:單線程事件循環(huán)(Event Loop). 結論先: 在JavaScript中, 利用運行至...
摘要:需要注意的是,定時器比較特殊,并沒有把回調函數(shù)掛在事件循環(huán)隊列中,它所做的就是設置一個定時器,當定時器到時后,環(huán)境會把你的回調函數(shù)放在事件循環(huán)中,這樣,在未來某個時刻的會被取出執(zhí)行。 Author: bugall Wechat: bugallF Email: [email protected] Github: https://github.com/bugall 一...
摘要:事件循環(huán)的順序,決定代碼執(zhí)行的順序。輸出第二輪事件循環(huán)正式結束三第三輪事件循環(huán)第三輪事件循環(huán)從宏任務開始。記為遇到,立即執(zhí)行回調函數(shù)放入中注冊,然后被分發(fā)到微任務事件隊列中。 1、為什么要有事件循環(huán)? 因為js是單線程的,事件循環(huán)是js的執(zhí)行機制,也是js實現(xiàn)異步的一種方法。 既然js是單線程,那就像只有一個窗口的銀行,客戶需要排隊一個一個辦理業(yè)務,同理js任務也要一個一個順序執(zhí)行。如...
閱讀 2571·2023-04-25 18:13
閱讀 793·2021-11-22 12:10
閱讀 2984·2021-11-22 11:57
閱讀 2148·2021-11-19 11:26
閱讀 2182·2021-09-22 15:40
閱讀 1474·2021-09-03 10:28
閱讀 2711·2019-08-30 15:53
閱讀 1959·2019-08-30 15:44