深入理解JavaScript中的宏任務(wù)和微任務(wù)機(jī)制
引入
// 開啟一個(gè)定時(shí)器 1秒后執(zhí)行 setTimeout( () => { console.log(1) }, 1000) console.log(2)
以上的代碼控制臺(tái)的輸出結(jié)果是“ 2,1";即先輸出2再輸出1;先執(zhí)行的是打印”2“的內(nèi)容,再執(zhí)行定時(shí)器中1秒之后打印”1"的內(nèi)容;
// 開啟一個(gè)定時(shí)器 1秒后執(zhí)行 setTimeout( () => { console.log(1) }, 0) console.log(2)
將定時(shí)器中的時(shí)間改為0,即立即觸發(fā),代碼控制臺(tái)的輸出結(jié)果仍然為"2, 1";定時(shí)器的作用是間隔一段時(shí)間后,將函數(shù)放入到任務(wù)隊(duì)列中,而 ”console.log(2)“則是直接在調(diào)用棧中執(zhí)行,執(zhí)行完畢了,消息隊(duì)列中的定時(shí)器才會(huì)進(jìn)入到調(diào)用棧中執(zhí)行代碼
宏任務(wù)與微任務(wù)
宏任務(wù)隊(duì)列
微任務(wù)隊(duì)列
棧與調(diào)用棧
JS是單線程的,它的運(yùn)行是基于時(shí)間循環(huán)機(jī)制(event loop)
調(diào)用棧
棧
棧是一種數(shù)據(jù)結(jié)構(gòu),后進(jìn)先出(l類比于彈夾中的子彈最后壓進(jìn)最先射出)
調(diào)用棧中,放的是要執(zhí)行的代碼
執(zhí)行的代碼在棧中從上到下依次執(zhí)行(上面的是左后進(jìn)去的,所以最先執(zhí)行)
消息隊(duì)列(任務(wù)隊(duì)列)
隊(duì)列 隊(duì)列是一種數(shù)據(jù)結(jié)構(gòu),先進(jìn)先出(類比于排隊(duì)打飯,排前面的先打)
隊(duì)列中,放的是將要執(zhí)行的代碼
當(dāng)調(diào)用棧中的代碼執(zhí)行完畢后,隊(duì)列中的代碼按照順序依次進(jìn)入到調(diào)用棧中執(zhí)行
在JS中任務(wù)隊(duì)列有兩種
宏任務(wù)隊(duì)列
大部分代碼都去宏任務(wù)隊(duì)列中排隊(duì)
微任務(wù)隊(duì)列
Promise的回調(diào)函數(shù)(then、catch、finally)在微任務(wù)隊(duì)列中排隊(duì)
整個(gè)流程
- 執(zhí)行調(diào)用棧中的代碼
- 執(zhí)行微任務(wù)中的所有任務(wù)
- 執(zhí)行宏任務(wù)中的所有任務(wù)
// 開啟一個(gè)定時(shí)器 1秒后執(zhí)行 setTimeout( () => { console.log(1) }, 0) Promise.resolve(1).then(() => { console.log(2) }) //全局作用域的代碼一定是位于棧中的 console.log(3)
上述代碼的執(zhí)行結(jié)果是”3 2 1“;打印"3”的語(yǔ)句是位于全局作用域中,一定是位于調(diào)用棧中的,所以最先執(zhí)行,
Promise的執(zhí)行原理
- Promise在執(zhí)行時(shí),then就相當(dāng)于給Promise了回調(diào)函數(shù)
- 當(dāng)promnise的狀態(tài)從pending 變?yōu)?fulfilled時(shí),then的回調(diào)函數(shù)會(huì)被放入任務(wù)隊(duì)列中
queueMicrotask() 用來(lái)向微任務(wù)隊(duì)列中添加一個(gè)任務(wù)
// 開啟一個(gè)定時(shí)器 1秒后執(zhí)行,在宏任務(wù)隊(duì)列中等待 setTimeout( () => { console.log(1) }, 0) // 在微任務(wù)隊(duì)列中等待 Promise.resolve(1).then(() => { console.log(2) }) // 在為任務(wù)隊(duì)列中 Promise.resolve().then(()=> { console.log(3) })
執(zhí)行結(jié)果是“2 3 1”;首先“2”和“3"是放在微任務(wù)中的,”1“是放在宏任務(wù)中,先執(zhí)行的應(yīng)該是微任務(wù)中的,隊(duì)列中遵循“先進(jìn)先出”,“2"先進(jìn)所以先出到調(diào)用棧中運(yùn)行,然后是”3“,最后是宏任務(wù)中的"1"
// 在微任務(wù)隊(duì)列中等待 Promise.resolve(1).then(() => { setTimeout( () => { console.log(1) }, 0) }) // 在為任務(wù)隊(duì)列中 Promise.resolve().then(()=> { console.log(3) })
執(zhí)行的結(jié)果是”3 1”;定時(shí)器的外層雖然是放在微任務(wù)的中,外層的Promise先執(zhí)行,但由于內(nèi)部有定時(shí)器隨后進(jìn)入了宏任務(wù),所以下面的“3”處于微任務(wù)中就直接進(jìn)入調(diào)用棧執(zhí)行了,所以執(zhí)行的結(jié)果為“31"
到此這篇關(guān)于深入理解JavaScript中的宏任務(wù)和微任務(wù)機(jī)制的文章就介紹到這了,更多相關(guān)JS中的宏任務(wù)和微任務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
微信小程序配置服務(wù)器提示驗(yàn)證token失敗的解決方法
這篇文章主要介紹了微信小程序配置服務(wù)器提示驗(yàn)證token失敗的解決方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-04-04使用layui 渲染table數(shù)據(jù)表格的實(shí)例代碼
今天小編就為大家分享一篇使用layui 渲染table數(shù)據(jù)表格的實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08javascript彈出一個(gè)層并增加一個(gè)覆蓋層
彈出一個(gè)層的js代碼,比較不錯(cuò)2008-10-10canvas簡(jiǎn)單快速的實(shí)現(xiàn)知乎登錄頁(yè)背景效果
本篇文章主要介紹了canvas簡(jiǎn)單快速實(shí)現(xiàn)知乎登錄頁(yè)背景效果的相關(guān)知識(shí),具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-05-05如何自己實(shí)現(xiàn)JavaScript的new操作符
new大家肯定都不陌生,單身沒(méi)有對(duì)象的時(shí)候就new一個(gè),很方便。那么它在創(chuàng)建實(shí)例的時(shí)候,具體做了哪些操作呢?今天我們就來(lái)一起分析一下。2021-04-04一個(gè)極為簡(jiǎn)單的requirejs實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇一個(gè)極為簡(jiǎn)單的requirejs實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10JS原型prototype和__proto__用法實(shí)例分析
這篇文章主要介紹了JS原型prototype和__proto__用法,結(jié)合實(shí)例形式分析了JS原型prototype和__proto__使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2020-03-03JS實(shí)現(xiàn)讀取xml內(nèi)容并輸出到div中的方法示例
這篇文章主要介紹了JS實(shí)現(xiàn)讀取xml內(nèi)容并輸出到div中的方法,涉及javascript針對(duì)xml格式數(shù)據(jù)的讀取、遍歷、輸出等相關(guān)操作技巧,需要的朋友可以參考下2018-04-04JavaScript Generator函數(shù)使用分析
生成器Generator是JavaScript ES6引入的特性,它讓我們可以分段執(zhí)行一個(gè)函數(shù)。但是在談?wù)撋善鳎℅enerator)之前,我們要先了解迭代器Iterator2022-10-10