5调用外部浏览器打开代码_浏览器事件循环
瀏覽器運行過程中會同時面對多種任務(wù),用戶交互事件(鼠標、鍵盤)、網(wǎng)絡(luò)請求、頁面渲染等。而這些任務(wù)不能是無序的,必須有個先來后到,瀏覽器內(nèi)部需要一套預(yù)定的邏輯來有序處理這些任務(wù),因此瀏覽器事件循環(huán)誕生了,再次強調(diào),是瀏覽器事件循環(huán),不是javascript事件循環(huán),js只是瀏覽器事件循環(huán)的參與者。
二、事件循環(huán)是什么
瀏覽器把任務(wù)區(qū)分成了 宏任務(wù) 和 微任務(wù) 或者叫 外部任務(wù) 和 內(nèi)部任務(wù) ,內(nèi)部任務(wù)可以理解為js內(nèi)部處理的任務(wù),外部任務(wù)可以認為是瀏覽器處理的任務(wù)。
外部隊列/宏任務(wù)隊列(Task Queue)
也可以叫宏任務(wù)隊列,瀏覽器中的外部事件源包含以下幾種:
- dom操作(頁面渲染)
- 用戶交互(鼠標、鍵盤)
- 網(wǎng)絡(luò)請求(Ajax等)
- History API操作(history.back、history.go...)
- 定時器(setTimeout)
這些外部事件源可能很多,為了方便瀏覽器廠商優(yōu)化,HTML標準中明確指出一個事件循環(huán)有一個或多個外部隊列,而每一個外部事件源都有一個對應(yīng)的外部隊列。不同的時間源之間可以有不同的優(yōu)先級(例如在網(wǎng)絡(luò)時間和用戶交互之間,瀏覽器可以優(yōu)先處理鼠標行為,從而讓用戶感覺更加流暢)。
內(nèi)部隊列/微任務(wù)隊列(Microtask Queue)
也可以叫微任務(wù)隊列,指的就是javascript語言內(nèi)部的事件隊列,在HTML標準中,并沒有明確規(guī)定這個隊列的事件源,通常認為有以下幾種:
- Promise的成功(.then)與失敗(.catch)
- MutationObserver
- Object.observe(已廢棄)
以上三種除了第一個,其他兩個可以認為沒有,實際上我們js中能夠使用的就只有promise。
事件循環(huán)模型
先來一張事件循環(huán)處理模型的截圖:
可以看出,每一個事件循環(huán),從外部任務(wù)隊列中拿出一個來執(zhí)行,執(zhí)行完一個外部任務(wù)后立即執(zhí)行內(nèi)部任務(wù)隊列中所有內(nèi)部任務(wù)(清空),然后瀏覽器執(zhí)行一次渲染,然后再次循環(huán)。
一段經(jīng)典代碼
了解了兩種隊列和事件循環(huán)的執(zhí)行模型,下面來一段經(jīng)典代碼:
// 以下代碼會得到什么樣的輸出結(jié)果?console.log('1');setTimeout(function() { console.log('2'); Promise.resolve().then(function() {console.log('3'); });}, 0);Promise.resolve().then(function() { console.log('4');}).then(function() { console.log('5');});console.log('6');答案是:164523
執(zhí)行順序如下:
- 由于執(zhí)行當前js代碼這個任務(wù)是一個宏任務(wù),因此首先輸出的是"1",
- 繼續(xù)執(zhí)行遇到setTimeou,由于setTimeout是一個外部事件源,它內(nèi)部的代碼會被push到TaskQueue中等待下一次事件循環(huán)再執(zhí)行,
- 當執(zhí)行到promise的 then 或 catchd的時候會將他們按順序追加到本輪事件循環(huán)的末尾,
- 再繼續(xù)往下執(zhí)行輸出6,宏任務(wù)完成后清空微任務(wù)隊列中的任務(wù),繼而輸出4、5
- 如果有的話,執(zhí)行渲染任務(wù)后,本次事件循環(huán)結(jié)束
- 開始執(zhí)行下一個宏任務(wù),也就是第一個setTimeout中的代碼塊,輸出2,然后將promise.then添加到本輪循環(huán)末尾
- 清空微任務(wù),輸出3
三、瀏覽器與Node.js的事件循環(huán)差異
區(qū)別對比
對于兩者的區(qū)別,來張瞟來的截圖:
這個例子的代碼如下:
setTimeout(()=>{ console.log('1'); Promise.resolve().then(function() {console.log('2'); });});setTimeout(()=>{ console.log('3'); Promise.resolve().then(function() {console.log('4'); });});這段代碼在瀏覽器和nodejs中的輸出結(jié)果分別是什么呢?
通過前面對瀏覽事件循環(huán)的了解,你應(yīng)該很容易得出在瀏覽器中的輸出結(jié)果是: 1234
那在nodejs中的輸出結(jié)果是什么呢?結(jié)果是在nodejs的 v11.x 之前輸出1324。這之間的原因是瀏覽器有非常多的用戶交互事件,為了用戶體驗更加流暢,必須均勻的處理宏任務(wù)和微任務(wù),而在nodejs中由于并沒有用戶交互事件,為了保證異步事件能夠被均等的執(zhí)行,因此設(shè)計的初衷就是先清空宏任務(wù)隊列再清空微任務(wù)隊列。
不過你應(yīng)該注意到,我上面只說了在 nodejs的 v11.x 之前輸出1324,但是nodejs這個特性在社區(qū)經(jīng)歷了一波開發(fā)者的吐槽之后,node官方在 v11 這個版本緊急修復(fù)了這個問題。所以在 v11.x 以上版本執(zhí)行以上代碼會得到在瀏覽器中一樣的結(jié)果。
setImmediate
先來張瞟來的截圖:
我們再來一個例子:
setTimeout(()=>{console.log('1');Promise.resolve().then(() => console.log('2'));});setTimeout(()=>{ console.log('3'); Promise.resolve().then(() => console.log('4'));});setImmediate(() => {console.log('5'); Promise.resolve().then(() => console.log('6'));});setImmediate(() => {console.log('7'); Promise.resolve().then(() => console.log('8'));});以上代碼在nodejsV13.x中的執(zhí)行結(jié)果是12345678,接下來我們把順序調(diào)換一下,在第二個位置插入setImmediate
setTimeout(()=>{console.log('1');Promise.resolve().then(() => console.log('2'));});setImmediate(() => {console.log('3'); Promise.resolve().then(() => console.log('4'));});setTimeout(()=>{ console.log('5'); Promise.resolve().then(() => console.log('6'));});setImmediate(() => {console.log('7'); Promise.resolve().then(() => console.log('8'));});執(zhí)行結(jié)果有一定的概率是12347856,也有一定的概率是 12563478
為啥不同的順序會得到不同的結(jié)果呢?這是由于setTImeout的精度問題導(dǎo)致的,到了這個級別的時間精度,代碼執(zhí)行的時間可能都會導(dǎo)致結(jié)果的不同。下面這張截圖是nodejs官方文檔對于事件循環(huán)順序的展示:
其中timers階段是用于執(zhí)行setTimeout事件的,check階段是用于執(zhí)行setImmediate事件的。Nodejs官方這個所謂事件循環(huán)過程,其實只是完整的事件循環(huán)中Node.js的多個外部隊列相互之間的優(yōu)先級。setTimeout是由event loop檢測系統(tǒng)時間是否到點然后向時間隊列插入一個事件,然后調(diào)用事件的回調(diào)方法。而setImmediate是監(jiān)控UI線程的調(diào)用棧,一旦調(diào)用棧為空則將回調(diào)壓棧。
講了這么多,其實對于上面setTimeout和setImmediate的對比結(jié)果還是有點模糊
推測:對于setImmediate的延時有時比setTimeout的要長,由于setImmediate要先監(jiān)控調(diào)用棧,若調(diào)用棧為空才壓棧,那么在壓棧之前event loop已經(jīng)將setTimeout事件的回調(diào)函數(shù)壓棧了。
好了,以上是這次分享的所有內(nèi)容,對于后面setTimeout和setImmedate的對比沒有的出一個明確的結(jié)果,有興趣的可以一起討論。
總結(jié)
以上是生活随笔為你收集整理的5调用外部浏览器打开代码_浏览器事件循环的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 网络服务器框架_Djang
- 下一篇: HTML页面代码移动端和pc兼容,pc端