標簽:點擊 解析 在線 imm 不同的 UNC 優先 自己 The
首先浏覽器是多進程的,每開啓一個網頁至少會開一個進程,開啓多個空白網頁,默認爲是一個進程,這個可以通過谷歌->更多工具->任務管理器查看(快捷鍵shift+Esc)
至于爲什麽是多進程是因爲如果浏覽器是單進程,那麽某個Tab頁崩潰了,就影響了整個浏覽器,體驗有多差;同理如果是單進程,插件崩潰了也會影響整個浏覽器;而且多進程還有其它的諸多優勢
1.浏覽器裏邊的進程
1.1、Brower進程:
浏覽器的主進程(負責協調、主控),只有一個;
作用:
a、負責浏覽器界面顯示,與用戶交互,如前進後退等;
b、負責各個頁面的管理,創建和銷毀其他進程;
c、將Renderer進程得到的內存中的Bitmap,繪制到用戶界面上;
d、網絡資源的管理,下載等;
1.2、第三方插件進程
每種類型的插件對應一個進程,僅當使用該插件時才創建;
1.3、GPU進程
最多一個,用于3D繪制等;
1.4、渲染進程(浏覽器內核)(Renderer進程,內部是多線程的)
a、默認每個Tab頁面是一個進程,互不影響;
b、主要作用爲頁面渲染,腳本執行,事件處理等;
其包含的线程有:GUI 渲染线程(负责渲染页面,解析 HTML,CSS 构成 DOM 树)、JS 引擎线程、事件觸發線程、定时器触发线程、http 请求线程等主要线程
GUI渲染線程
負責渲染浏覽器界面,解析HTML,CSS,構建DOM樹和RenderObject樹,布局和繪制等。
當界面需要重繪(Repaint)或由于某種操作引發回流(reflow)時,該線程就會執行
注意,GUI渲染線程与JS引擎線程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
JS引擎線程
也稱爲JS內核,負責處理Javascript腳本程序。(例如V8引擎)
JS引擎線程负责解析Javascript脚本,运行代码。
JS引擎一直等待著任務隊列中任務的到來,然後加以處理,一個Tab頁(renderer進程)中無論什麽時候都只有一個JS線程在運行JS程序
同样注意,GUI渲染線程与JS引擎線程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
事件觸發線程
歸屬于浏覽器而不是JS引擎,用來控制事件循環(可以理解,JS引擎自己都忙不過來,需要浏覽器另開線程協助)
当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标點擊、AJAX异步请求等),会将对应任务添加到事件线程中
當對應的事件符合觸發條件被觸發時,該線程會把事件添加到待處理隊列的隊尾,等待JS引擎的處理
注意,由于JS的單線程關系,所以這些待處理隊列中的事件都得排隊等待JS引擎處理(當JS引擎空閑時才會去執行)
定時觸發器線程
传说中的setInterval与setTimeout所在線程
浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
因此通過單獨線程來計時並觸發定時(計時完畢後,添加到事件隊列中,等待JS引擎空閑後執行)
注意,W3C在HTML標准中規定,規定要求setTimeout中低于4ms的時間間隔算爲4ms。
異步http請求線程
在XMLHttpRequest在連接後是通過浏覽器新開一個線程請求
將檢測到狀態變更時,如果設置有回調函數,異步線程就産生狀態變更事件,將這個回調再放入事件隊列中。再由JavaScript引擎執行。
事件循環
主线程:也就是 js 引擎执行的线程,这个线程只有一个,页面渲染、函数处理都在这个主线程上执行。
工作線程:也稱幕後線程,這個線程可能存在于浏覽器或js引擎內,與主線程是分開的,處理文件讀取、網絡請求等異步事件。
任务队列( Event Queue )
所有的任务可以分为同步任务和异步任务,同步任务,顾名思义,就是立即执行的任务,同步任务一般会直接进入到主线程中执行;而异步任务,就是异步执行的任务,比如ajax网络请求,setTimeout 定时函数等都属于异步任务,异步任务会通过任务队列的机制(先进先出的机制)来进行协调。具体的可以用下面的图来大致说明一下:
在事件循環中,每进行一次循环操作称为tick,通过阅读规范可知,每一次 tick 的任务处理模型是比较复杂的,其关键的步骤可以總結如下:
可以用一張圖來說明下流程:
这里相信有人会想问,什么是 microtasks ?规范中规定,task分为两大类, 分别是 Macro Task (宏任务)和 Micro Task(微任务), 并且每个宏任务结束后, 都要清空所有的微任务,这里的 Macro Task也是我们常说的 task ,有些文章并没有对其做区分,后面文章中所提及的task皆看做宏任务( macro task)。
setTimeout/Promise 等API便是任务源,而进入任务队列的是由他们指定的具体执行任务。来自不同任务源的任务会进入到不同的任务队列。其中 setTimeout 与 setInterval 是同源的。
掌握概念之後,我們來做一個例子強化一下:
console.log(‘script start‘);
setTimeout(function() {
console.log(‘setTimeout‘);
}, 0);
Promise.resolve().then(function() {
console.log(‘promise1‘);
}).then(function() {
console.log(‘promise2‘);
});
console.log(‘script end‘);
整体 script 作为第一个宏任务进入主线程,遇到 console.log,输出 script start
遇到 setTimeout,其回调函数被分发到宏任务 Event Queue 中
遇到 Promise,其 then函数被分到到微任务 Event Queue 中,记为 then1,之后又遇到了 then 函数,将其分到微任务 Event Queue 中,记为 then2
遇到 console.log,输出 script end
执行微任务,首先执行then1,输出 promise1, 然后执行 then2,输出 promise2,这样就清空了所有微任务
执行 setTimeout 任务,输出 setTimeout 至此,输出的顺序是:script start, script end, promise1, promise2, setTimeout
再來一個題目,來做個練習:
console.log(‘script start‘);
setTimeout(function() {
console.log(‘timeout1‘);
}, 10);
new Promise(resolve => {
console.log(‘promise1‘);
resolve();
setTimeout(() => console.log(‘timeout2‘), 10);
}).then(function() {
console.log(‘then1‘)
})
console.log(‘script end‘);
這個題目就稍微有點複雜了,我們再分析下:
首先,事件循環从宏任务 (macrotask) 队列开始,最初始,宏任务队列中,只有一个 scrip t(整体代码)任务;当遇到任务源 (task source) 时,则会先分发任务到对应的任务队列中去。所以,就和上面例子类似,首先遇到了console.log,输出 script start; 接着往下走,遇到 setTimeout 任务源,将其分发到任务队列中去,记为 timeout1; 接着遇到 promise,new promise 中的代码立即执行,输出 promise1, 然后执行 resolve ,遇到 setTimeout ,将其分发到任务队列中去,记为 timemout2, 将其 then 分发到微任务队列中去,记为 then1; 接着遇到 console.log 代码,直接输出 script end 接着检查微任务队列,发现有个 then1 微任务,执行,输出then1 再检查微任务队列,发现已经清空,则开始检查宏任务队列,执行 timeout1,输出 timeout1; 接着执行 timeout2,输出 timeout2 至此,所有的都队列都已清空,执行完毕。其输出的顺序依次是:script start, promise1, script end, then1, timeout1, timeout2
用流程圖看更清晰:
有个小 tip:从规范来看,microtask 優先于 task 执行,所以如果有需要優先执行的逻辑,放入microtask 队列会比 task 更早的被执行。
標簽:點擊 解析 在線 imm 不同的 UNC 優先 自己 The
原文地址:https://www.cnblogs.com/huanxiongs02/p/14772016.html