- 為什么js是單線程,web works是多線程?
- 為什么js單線程卻擁有異步饭尝?
- event loop募闲?
- 為什么setTimeout時(shí)間時(shí)間不準(zhǔn)確
瀏覽器的主要功能就是向服務(wù)器發(fā)出請(qǐng)求,在瀏覽器窗口中展示您選擇的網(wǎng)絡(luò)資源罗捎。這里所說(shuō)的資源一般是指 HTML 文檔观谦,也可以是 PDF、圖片或其他的類型桨菜。資源的位置由用戶使用 URI(統(tǒng)一資源標(biāo)示符)指定豁状。
瀏覽器的高層結(jié)構(gòu)
用戶界面:地址欄,瀏覽器頁(yè)面前進(jìn)后退倒得,刷新和停止刷新按鈕泻红,書簽...
瀏覽器引擎:用戶界面與呈現(xiàn)引擎之間傳送指令
呈現(xiàn)引擎:顯示請(qǐng)求內(nèi)容,解析html&css
網(wǎng)絡(luò):網(wǎng)絡(luò)調(diào)用屎暇。
用戶界面后端:用戶繪制基本的小窗口部件
JS解釋器:用于解析和執(zhí)行js代碼
數(shù)據(jù)存儲(chǔ):瀏覽器在硬盤上保存的各種數(shù)據(jù)承桥。
瀏覽器是多進(jìn)程的
瀏覽器設(shè)置中可以找到瀏覽器的任務(wù)管理
可以看每個(gè)頁(yè)面都是獨(dú)立的進(jìn)程,
進(jìn)程分一下幾種:
- 主進(jìn)程:主進(jìn)程根悼,只有一個(gè)
- 瀏覽器渲染進(jìn)程:進(jìn)程之間互不影響
- 第三方插件進(jìn)程:使用時(shí)創(chuàng)建進(jìn)程
- GPU進(jìn)程:用于3D繪制
瀏覽器輸入網(wǎng)頁(yè)之后發(fā)生了什么凶异?
呈現(xiàn)引擎示意圖(webkit舉例)
呈現(xiàn)引擎解析html文檔,講標(biāo)記轉(zhuǎn)化成‘樹’挤巡,同時(shí)解析外包c(diǎn)ss文件剩彬。
創(chuàng)建呈現(xiàn)樹,包含視覺屬性(顏色和尺寸)矿卑,排列順序就是屏幕展示順序
呈現(xiàn)樹中每個(gè)節(jié)點(diǎn)應(yīng)該出現(xiàn)在屏幕的確切坐標(biāo)
為了更好的用戶體驗(yàn)喉恋,引擎會(huì)力求盡快將內(nèi)容展示在屏幕上。
解析器遇到<script>標(biāo)記時(shí)會(huì)立即解析腳本母廷,直到資源加載完成轻黑,也可以通過(guò)添加’defer‘,這樣不會(huì)停止文檔解析琴昆。
網(wǎng)絡(luò)操作與以上同時(shí)進(jìn)行氓鄙。
js解析,由單獨(dú)的腳本引擎解析執(zhí)行业舍,通常狀態(tài)是動(dòng)態(tài)的改變dom樹抖拦,如果js是多線程升酣,當(dāng)兩個(gè)線程同時(shí)修改同一個(gè)dom節(jié)點(diǎn),下達(dá)兩個(gè)矛盾的命令時(shí)态罪,瀏覽器的執(zhí)行就成了問(wèn)題
以上解決了為什么js是單線程噩茄,web works是多線程?
為什么js單線程卻擁有異步复颈?
單線程是指cpu一次只能做一件事绩聘,js在任務(wù)隊(duì)列中提取任務(wù)放到主任務(wù)中執(zhí)行,瀏覽器為異步任務(wù)單獨(dú)開辟線程耗啦。就不得不說(shuō)到Event Loop君纫。
Event Loop
JS是單線程的,當(dāng)遇到大量任務(wù)或耗時(shí)任務(wù)時(shí)會(huì)卡死芹彬,線程大部分事件都在空等I/O的返回結(jié)果蓄髓。這種方式稱之為’同步‘。
Event Loop解決了此類問(wèn)題舒帮,在程序中設(shè)置兩個(gè)線程会喝,一個(gè)主線程,負(fù)責(zé)程序本身的運(yùn)行玩郊,一個(gè)另一個(gè)負(fù)責(zé)與其他線程之間的通信肢执。
首先要了解常用宏任務(wù),微任務(wù)
宏任務(wù):
- setTimeout
- setInterval
微任務(wù) - Promise
- nextTick(node)
Event Loop流程: - 執(zhí)行全局同步代碼译红,調(diào)用棧清空
- 取出微任務(wù)隊(duì)列首位任務(wù)预茄,放入調(diào)用棧中。直到全部微任務(wù)執(zhí)行完成侦厚。
- 取出宏任務(wù)隊(duì)列首位任務(wù)耻陕,放入調(diào)用棧中執(zhí)行,其中產(chǎn)生的微任務(wù)放入微任務(wù)隊(duì)列刨沦,宏任務(wù)同理诗宣。
- 重復(fù)二三步操作,直到全部執(zhí)行完成想诅。
setTimeout(()=>{
console.log('time1');
Promise.resolve().then(()=>{
console.log('p1')
})
})
setTimeout(()=>{
console.log('time2');
Promise.resolve().then(()=>{
console.log('p2')
})
})
如圖
setTimeout時(shí)間時(shí)間不準(zhǔn)確
:很多原因都會(huì)導(dǎo)致setTimeout 沒(méi)有按時(shí)執(zhí)行召庞,原因之一異步執(zhí)行setTimeout,將setTimeout放入宏任務(wù)棧隊(duì)列来破,等執(zhí)行到宏任務(wù)隊(duì)列時(shí)篮灼,代碼移出本輪事件循環(huán),等到下一輪事件循環(huán)徘禁,再檢查是否到了指定時(shí)間诅诱。所以會(huì)有一定程度的延遲。
var date = new Date();
setTimeout(()=>{
new Promise(
function (resolve, reject) {
setTimeout(resolve)
}
).then(()=>{
console.log(new Date()-date,'p');
})
})
setTimeout(function(e) {
console.log(new Date()-date,'s');
});
輸出1 "s" 3 'p'
雖然setTimeout中沒(méi)有設(shè)置時(shí)間晌坤,但還是延遲執(zhí)行了一段時(shí)間逢艘。
在瀏覽器中,
setTimeout()/setInterval()
的每調(diào)用一次定時(shí)器的最小間隔是4ms(摘自MDN)骤菠,也會(huì)導(dǎo)致setTimeout沒(méi)有按時(shí)執(zhí)行
例如:
var date = new Date();
setTimeout(function() {
setTimeout(function() {
setTimeout(function() {
setTimeout(function() {
setTimeout(function() {
setTimeout(function() {
console.log(new Date()-date);
}, 0);
}, 0);
}, 0);
}, 0);
}, 0);
}, 0);
如果每個(gè)間隔是4ms它改,輸出應(yīng)該是20,實(shí)際輸出是16 17 ...商乎,實(shí)踐證明其實(shí)間隔不一定是4ms
setTimeout(function(){console.log('4');},4)
setTimeout(function(){console.log('3');},3)
setTimeout(function(){console.log('2');},2)
setTimeout(function(){console.log('1');},1)
setTimeout(function(){console.log('0');},0)
以上代碼你覺得會(huì)輸出什么央拖?
實(shí)際輸出 1 0 2 3 4,不同瀏覽器輸出結(jié)果不同鹉戚,每次輸出結(jié)果也有可能不同鲜戒。
是因?yàn)椋瑂etTimeout在宏任務(wù)隊(duì)列中抹凳,主線程按順序執(zhí)行宏任務(wù)隊(duì)列遏餐,發(fā)現(xiàn)沒(méi)有可以執(zhí)行的宏任務(wù),遍歷隊(duì)伍有沒(méi)有可以執(zhí)行的任務(wù)赢底,4ms時(shí)間還沒(méi)到失都,每次掛起一個(gè)settimeout的時(shí)候都會(huì)循環(huán)一遍事件隊(duì)列。1比0先執(zhí)行是因?yàn)?先被掛起幸冻。
參考資料
瀏覽器的工作原理:新式網(wǎng)絡(luò)瀏覽器幕后揭秘:https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/#Event_loop
什么是 Event Loop粹庞?阮一峰:
http://www.ruanyifeng.com/blog/2013/10/event_loop.html
MDN:
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/setTimeout