中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

javascript瀏覽器中事件循環機制的示例分析

發布時間:2021-01-21 14:45:55 來源:億速云 閱讀:134 作者:小新 欄目:web開發

這篇文章主要介紹javascript瀏覽器中事件循環機制的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

拋在前面的問題:

單線程如何做到異步

事件循環的過程是怎樣的

macrotask 和 microtask 是什么,它們有何區別

單線程和異步

提到js,就會想到單線程,異步,那么單線程是如何做到異步的呢?概念先行,先要了解下單線程和異步之間的關系。

js的任務分為 同步 和 異步 兩種,它們的處理方式也不同,同步任務是直接在主線程上排隊執行,異步任務則會被放到任務隊列中,若有多個任務(異步任務)則要在任務隊列中排隊等待,任務隊列類似一個緩沖區,任務下一步會被移到調用棧(call stack),然后主線程執行調用棧的任務。

單線程是指js引擎中負責解析執行js代碼的線程只有一個(主線程),即每次只能做一件事,而我們知道一個ajax請求,主線程在等待它響應的同時是會去做其它事的,瀏覽器先在事件表注冊ajax的回調函數,響應回來后回調函數被添加到任務隊列中等待執行,不會造成線程阻塞,所以說js處理ajax請求的方式是異步的。

總而言之,檢查調用棧是否為空,以及確定把哪個task加入調用棧的這個過程就是事件循環,而js實現異步的核心就是事件循環。

調用棧和任務隊列

顧名思義,調用棧是一個棧結構,函數調用會形成一個棧幀,幀中包含了當前執行函數的參數和局部變量等上下文信息,函數執行完后,它的執行上下文會從棧中彈出。

下圖就是調用棧和任務隊列的關系圖

javascript瀏覽器中事件循環機制的示例分析

事件循環

關于事件循環,HTML規范的介紹

There must be at least one event loop per user agent, and at most one event loop per unit of related similar-origin browsing contexts.
An event loop has one or more task queues.
Each task is defined as coming from a specific task source.

從規范理解,瀏覽器至少有一個事件循環,一個事件循環至少有一個任務隊列(macrotask),每個外任務都有自己的分組,瀏覽器會為不同的任務組設置優先級。

macrotask & microtask

規范有提到兩個概念,但沒有詳細介紹,查閱一些資料大概可總結如下:

macrotask:包含執行整體的js代碼,事件回調,XHR回調,定時器(setTimeout/setInterval/setImmediate),IO操作,UI render

microtask:更新應用程序狀態的任務,包括promise回調,MutationObserver,process.nextTick,Object.observe

其中setImmediate和process.nextTick是nodejs的實現,在nodejs篇會詳細介紹。

事件處理過程

關于macrotask和microtask的理解,光這樣看會有些晦澀難懂,結合事件循壞的機制理解清晰很多,下面這張圖可以說是介紹得非常清楚了。

javascript瀏覽器中事件循環機制的示例分析

總結起來,一次事件循環的步驟包括:

1. 檢查macrotask隊列是否為空,非空則到2,為空則到3
2. 執行macrotask中的一個任務
3. 繼續檢查microtask隊列是否為空,若有則到4,否則到5
4. 取出microtask中的任務執行,執行完成返回到步驟3
5. 執行視圖更新

mactotask & microtask的執行順序

javascript瀏覽器中事件循環機制的示例分析

讀完這么多干巴巴的概念介紹,還不如看一段代碼感受下

console.log('start')
setTimeout(function() {
console.log('setTimeout')
}, 0)
Promise.resolve().then(function() {
console.log('promise1')
}).then(function() {
console.log('promise2')
})
console.log('end')

打印臺輸出的log順序是什么?結合上述的步驟分析,系不系so easy~

javascript瀏覽器中事件循環機制的示例分析

首先,全局代碼(main())壓入調用棧執行,打印start;

接下來setTimeout壓入macrotask隊列,promise.then回調放入microtask隊列,最后執行console.log(‘end’),打印出end;

至此,調用棧中的代碼被執行完成,回顧macrotask的定義,我們知道全局代碼屬于macrotask,macrotask執行完,那接下來就是執行microtask隊列的任務了,執行promise回調打印promise1;

promise回調函數默認返回undefined,promise狀態變為fullfill觸發接下來的then回調,繼續壓入microtask隊列,event loop會把當前的microtask隊列一直執行完,此時執行第二個promise.then回調打印出promise2;

這時microtask隊列已經為空,從上面的流程圖可以知道,接下來主線程會去做一些UI渲染工作(不一定會做),然后開始下一輪event loop,執行setTimeout的回調,打印出setTimeout;

這個過程會不斷重復,也就是所謂的事件循環。

視圖渲染的時機

回顧上面的事件循環示意圖,update rendering(視圖渲染)發生在本輪事件循環的microtask隊列被執行完之后,也就是說執行任務的耗時會影響視圖渲染的時機。通常瀏覽器以每秒60幀(60fps)的速率刷新頁面,據說這個幀率最適合人眼交互,大概16.7ms渲染一幀,所以如果要讓用戶覺得順暢,單個macrotask及它相關的所有microtask最好能在16.7ms內完成。

但也不是每輪事件循環都會執行視圖更新,瀏覽器有自己的優化策略,例如把幾次的視圖更新累積到一起重繪,重繪之前會通知requestAnimationFrame執行回調函數,也就是說requestAnimationFrame回調的執行時機是在一次或多次事件循環的UI render階段。

以下代碼可以驗證

setTimeout(function() {console.log('timer1')}, 0)
requestAnimationFrame(function(){
	console.log('requestAnimationFrame')
	})
	setTimeout(function() {console.log('timer2')}, 0)
	new Promise(function executor(resolve) {
	console.log('promise 1')
	resolve()
	console.log('promise 2')
	}).then(function() {
	console.log('promise then')
	})
	console.log('end')

javascript瀏覽器中事件循環機制的示例分析

javascript瀏覽器中事件循環機制的示例分析

可以看到,結果1中requestAnimationFrame()是在一次事件循環后執行,而在結果2,它的執行則是在三次事件循環結束后。

總結

1、事件循環是js實現異步的核心

2、每輪事件循環分為3個步驟:

a) 執行macrotask隊列的一個任務
b) 執行完當前microtask隊列的所有任務
c) UI render

3、瀏覽器只保證requestAnimationFrame的回調在重繪之前執行,沒有確定的時間,何時重繪由瀏覽器決定

以上是“javascript瀏覽器中事件循環機制的示例分析”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

聊城市| 杂多县| 旌德县| 长宁县| 仪征市| 涡阳县| 白沙| 新化县| 繁昌县| 阿合奇县| 岑溪市| 手游| 汉寿县| 宣威市| 扎兰屯市| 宜春市| 上栗县| 惠州市| 修武县| 高要市| 大庆市| 象山县| 友谊县| 繁峙县| 静安区| 灵石县| 江达县| 郸城县| 永吉县| 勐海县| 台前县| 临安市| 来宾市| 哈尔滨市| 阜康市| 岳阳县| 蛟河市| 彭州市| 廊坊市| 青田县| 光泽县|