首先,我想說(shuō)一下Javascript的裝載和執(zhí)行。
通常來(lái)說(shuō),瀏覽器對(duì)于Javascript的運(yùn)行有兩大特性:
1)載入后馬上執(zhí)行,
2)執(zhí)行時(shí)會(huì)阻塞頁(yè)面后續(xù)的內(nèi)容(包括頁(yè)面的渲染、其它資源的下載)**。于是,如果有多個(gè)js文件被引入,那么對(duì)于瀏覽器來(lái)說(shuō),這些js文件被被串行地載入,并依次執(zhí)行。
因?yàn)閖avascript可能會(huì)來(lái)操作HTML文檔的DOM樹,所以,瀏覽器一般都不會(huì)像并行下載css文件并行下載js文件,因?yàn)檫@是js文件的特殊性造成的。
所以,如果你的javascript想操作后面的DOM元素,基本上來(lái)說(shuō),瀏覽器都會(huì)報(bào)錯(cuò)說(shuō)對(duì)象找不到。因?yàn)镴avascript執(zhí)行時(shí),后面的HTML被阻塞住了,DOM樹時(shí)還沒(méi)有后面的DOM結(jié)點(diǎn)。所以程序也就報(bào)錯(cuò)了。
有沒(méi)有辦法實(shí)現(xiàn)只加載不立即執(zhí)行,后續(xù)按需執(zhí)行呢?
http://www.linjunlong.com/p/1156.html 這里有一種方案。
下面說(shuō)說(shuō)具體執(zhí)行:
-
棧和隊(duì)列
image.png
在上圖中,調(diào)用棧中遇到DOM操作、ajax請(qǐng)求以及setTimeout等WebAPIs的時(shí)候就會(huì)交給瀏覽器內(nèi)核的其他模塊進(jìn)行處理,webkit內(nèi)核在Javasctipt執(zhí)行引擎之外,有一個(gè)重要的模塊是webcore模塊。對(duì)于圖中WebAPIs提到的三種API,webcore分別提供了DOM Binding、network、timer模塊來(lái)處理底層實(shí)現(xiàn)。等到這些模塊處理完這些操作的時(shí)候?qū)⒒卣{(diào)函數(shù)放入任務(wù)隊(duì)列中,之后等棧中的task執(zhí)行完之后再去執(zhí)行任務(wù)隊(duì)列之中的回調(diào)函數(shù)
可以看出:
所有的代碼都要通過(guò)函數(shù)調(diào)用棧中調(diào)用執(zhí)行。
當(dāng)遇到前文中提到的APIs的時(shí)候,會(huì)交給瀏覽器內(nèi)核的其他模塊進(jìn)行處理。
任務(wù)隊(duì)列中存放的是回調(diào)函數(shù)。
等到調(diào)用棧中的task執(zhí)行完之后再回去執(zhí)行任務(wù)隊(duì)列之中的task。
- 整個(gè)事件循環(huán)機(jī)制包含兩種事件隊(duì)列
(function test() {
/*開始執(zhí)行本次macro-task*/
setTimeout(function() {console.log(4)}, 0);----作為一個(gè)macro-task,將其回調(diào)函數(shù)放入自己的隊(duì)列之中。
new Promise(function executor(resolve) {
console.log(1);---- 立即執(zhí)行
for( var i=0 ; i<10000 ; i++ ) {
i == 9999 && resolve();
}
console.log(2);---- 立即執(zhí)行
}).then(function() {
console.log(5);---micro task queue
});
console.log(3);---- 立即執(zhí)行
})()
macro-task包括:script(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering。
micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver
事件循環(huán)的順序是從script開始第一次循環(huán),隨后全局上下文進(jìn)入函數(shù)調(diào)用棧,碰到macro-task就將其交給處理它的模塊處理完之后將回調(diào)函數(shù)放進(jìn)macro-task的隊(duì)列之中,碰到micro-task也是將其回調(diào)函數(shù)放進(jìn)micro-task的隊(duì)列之中。直到函數(shù)調(diào)用棧清空只剩全局執(zhí)行上下文,然后開始執(zhí)行所有的micro-task。當(dāng)所有可執(zhí)行的micro-task執(zhí)行完畢之后。循環(huán)再次執(zhí)行macro-task中的一個(gè)任務(wù)隊(duì)列,執(zhí)行完之后再執(zhí)行所有的micro-task,就這樣一直循環(huán)。
總結(jié)
不同的任務(wù)會(huì)放進(jìn)不同的任務(wù)隊(duì)列之中。
先執(zhí)行macro-task,等到函數(shù)調(diào)用棧清空之后再執(zhí)行所有在隊(duì)列之中的micro-task。
等到所有micro-task執(zhí)行完之后再?gòu)膍acro-task中的一個(gè)任務(wù)隊(duì)列開始執(zhí)行,就這樣一直循環(huán)。
當(dāng)有多個(gè)macro-task(micro-task)隊(duì)列時(shí),事件循環(huán)的順序是按上文macro-task(micro-task)的分類中書寫的順序執(zhí)行的。
