任務(wù)隊(duì)列,事件循環(huán) 宏任務(wù) 微任務(wù)

javascript執(zhí)行機(jī)制是基于事件循環(huán)的并發(fā)式的,事件循環(huán)負(fù)責(zé)處理代碼,收集和處理事件以及執(zhí)行隊(duì)列中的子任務(wù)


可視化描述

棧(stack)

js運(yùn)行時(shí)形成一個(gè)執(zhí)行棧,

function foo(b) {
  let a = 10;
  return a + b + 11;
}

function bar(x) {
  let y = 3;
  return foo(x * y);
}

console.log(bar(7)); // 返回 42

當(dāng)調(diào)用 bar 時(shí)氛改,第一個(gè)幀被創(chuàng)建并壓入棧中,幀中包含了 bar 的參數(shù)和局部變量。 當(dāng) bar 調(diào)用 foo 時(shí)佩憾,第二個(gè)幀被創(chuàng)建并被壓入棧中沸停,放在第一個(gè)幀之上嵌巷,幀中包含 foo 的參數(shù)和局部變量衣屏。當(dāng) foo 執(zhí)行完畢然后返回時(shí)施绎,第二個(gè)幀就被彈出棧(剩下 bar 函數(shù)的調(diào)用幀 )耕赘。當(dāng) bar 也執(zhí)行完畢然后返回時(shí)骄蝇,第一個(gè)幀也被彈出,棧就被清空了操骡。

堆(heap)

對(duì)象被分配在堆中九火,堆是一個(gè)用來表示一大塊(通常是非結(jié)構(gòu)化的)內(nèi)存區(qū)域的計(jì)算機(jī)術(shù)語赚窃。

隊(duì)列(queue)

一個(gè) JavaScript 運(yùn)行時(shí)包含了一個(gè)待處理消息的消息隊(duì)列。每一個(gè)消息都關(guān)聯(lián)著一個(gè)用以處理這個(gè)消息的回調(diào)函數(shù)岔激。

setTimeout(function(){console.log('時(shí)間一到,請(qǐng)執(zhí)行改回調(diào)函數(shù)')},1000)

事件循環(huán) 期間的某個(gè)時(shí)刻勒极,運(yùn)行時(shí)會(huì)從最先進(jìn)入隊(duì)列的消息開始處理隊(duì)列中的消息。被處理的消息會(huì)被移出隊(duì)列虑鼎,并作為輸入?yún)?shù)來調(diào)用與之關(guān)聯(lián)的函數(shù)辱匿。正如前面所提到的,調(diào)用一個(gè)函數(shù)總是會(huì)為其創(chuàng)造一個(gè)新的棧幀炫彩。

事件循環(huán)(event loop)

queue.waitForMessage() 會(huì)同步地等待消息到達(dá)(如果當(dāng)前沒有任何消息等待被處理)

while (queue.waitForMessage()) {
queue.processNextMessage();
}

特點(diǎn)

  1. 單線程

每一個(gè)消息完整地執(zhí)行后匾七,其它消息才會(huì)被執(zhí)行
缺點(diǎn):當(dāng)一個(gè)消息需要太長(zhǎng)時(shí)間才能處理完畢時(shí),Web應(yīng)用程序就無法處理與用戶的交互江兢,例如點(diǎn)擊或滾動(dòng)
2.永不阻塞
JavaScript的事件循環(huán)模型與許多其他語言不同的一個(gè)非常有趣的特性是乐尊,它永不阻塞。 處理 I/O 通常通過事件和回調(diào)來執(zhí)行划址,所以當(dāng)一個(gè)應(yīng)用正等待一個(gè) IndexedDB 查詢返回或者一個(gè) XHR 請(qǐng)求返回時(shí)扔嵌,它仍然可以處理其它事情,比如用戶輸入夺颤。

總結(jié)一下事件循環(huán)的機(jī)制:

(1) 所有任務(wù)在執(zhí)行棧上執(zhí)行,
(2) 綁定事件和異步事件(消息)放置于任務(wù)隊(duì)列
(3) 執(zhí)行棧執(zhí)行完畢,一直詢問任務(wù)隊(duì)列是否有消息需要執(zhí)行,如果有就將關(guān)聯(lián)的回調(diào)函數(shù)放置于執(zhí)行棧,準(zhǔn)備執(zhí)行

宏任務(wù)(macrotask)和微任務(wù)(mircrotask)

異步任務(wù)細(xì)分為宏任務(wù)和微任務(wù),當(dāng)一個(gè)宏任務(wù)執(zhí)行完痢缎,會(huì)在渲染前,將執(zhí)行期間所產(chǎn)生的所有微任務(wù)都執(zhí)行完

宏任務(wù) -> 微任務(wù) -> GUI渲染 -> 宏任務(wù) -> ...

宏任務(wù):

  • 主代碼塊
  • setTimeout
  • setTimeInterval
  • setImmediate()-node
  • requestAnimationFrame -瀏覽器
  • postMessage
  • I/O
  • UI交互事件
    微任務(wù):
  • process.nextTick ()-Node
  • Promise.then()
  • catch
  • finally
  • Object.observe
  • MutationObserver
圖解宏任務(wù)和微任務(wù)

注意點(diǎn)

  1. 瀏覽器會(huì)先執(zhí)行一個(gè)宏任務(wù)世澜,緊接著執(zhí)行當(dāng)前執(zhí)行棧產(chǎn)生的微任務(wù)独旷,再進(jìn)行渲染,然后再執(zhí)行下一個(gè)宏任務(wù)
  2. 微任務(wù)和宏任務(wù)不在一個(gè)任務(wù)隊(duì)列

總結(jié)

*執(zhí)行主線程,遇到異步置入任務(wù)隊(duì)列,并在任務(wù)隊(duì)列根據(jù)宏任務(wù)和微任務(wù)進(jìn)行區(qū)分
*執(zhí)行棧完成,查看任務(wù)隊(duì)列,首先查看宏任務(wù)隊(duì)列有沒有要執(zhí)行的任務(wù),沒有就過,有就執(zhí)行

  • 每個(gè)宏任務(wù)執(zhí)行完都要查看微任務(wù)隊(duì)列,有沒有要執(zhí)行的任務(wù),沒有就過,有就執(zhí)行,直到微任務(wù)隊(duì)列為空

測(cè)試:

function test() {
  console.log(1)
  setTimeout(function () {  // timer1
    console.log(2)
  }, 1000)
}

test();

setTimeout(function () {        // timer2
  console.log(3)
})

new Promise(function (resolve) {
  console.log(4)
  setTimeout(function () {  // timer3
    console.log(5)
  }, 100)
  resolve()
}).then(function () {
  setTimeout(function () {  // timer4
    console.log(6)
  }, 0)
  console.log(7)
})

console.log(8)

結(jié)合我們上述的JS運(yùn)行機(jī)制再來看這道題就簡(jiǎn)單明了的多了
JS是順序從上而下執(zhí)行
執(zhí)行到test()寥裂,test方法為同步嵌洼,直接執(zhí)行,console.log(1)打印1
test方法中setTimeout為異步宏任務(wù)封恰,回調(diào)我們把它記做timer1放入宏任務(wù)隊(duì)列
接著執(zhí)行麻养,test方法下面有一個(gè)setTimeout為異步宏任務(wù),回調(diào)我們把它記做timer2放入宏任務(wù)隊(duì)列
接著執(zhí)行promise诺舔,new Promise是同步任務(wù)鳖昌,直接執(zhí)行,打印4
new Promise里面的setTimeout是異步宏任務(wù)低飒,回調(diào)我們記做timer3放到宏任務(wù)隊(duì)列
Promise.then是微任務(wù)许昨,放到微任務(wù)隊(duì)列
console.log(8)是同步任務(wù),直接執(zhí)行褥赊,打印8
主線程任務(wù)執(zhí)行完畢糕档,檢查微任務(wù)隊(duì)列中有Promise.then
開始執(zhí)行微任務(wù),發(fā)現(xiàn)有setTimeout是異步宏任務(wù)拌喉,記做timer4放到宏任務(wù)隊(duì)列
微任務(wù)隊(duì)列中的console.log(7)是同步任務(wù)速那,直接執(zhí)行俐银,打印7
微任務(wù)執(zhí)行完畢,第一次循環(huán)結(jié)束
檢查宏任務(wù)隊(duì)列琅坡,里面有timer1、timer2残家、timer3榆俺、timer4,四個(gè)定時(shí)器宏任務(wù)坞淮,按照定時(shí)器延遲時(shí)間得到可以執(zhí)行的順序茴晋,即Event Queue:timer2、timer4回窘、timer3诺擅、timer1,依次拿出放入執(zhí)行棧末尾執(zhí)行 (插播一條:瀏覽器 event loop 的 Macrotask queue啡直,就是宏任務(wù)隊(duì)列在每次循環(huán)中只會(huì)讀取一個(gè)任務(wù))
執(zhí)行timer2烁涌,console.log(3)為同步任務(wù),直接執(zhí)行酒觅,打印3
檢查沒有微任務(wù)撮执,第二次Event Loop結(jié)束
執(zhí)行timer4,console.log(6)為同步任務(wù)舷丹,直接執(zhí)行抒钱,打印6
檢查沒有微任務(wù),第三次Event Loop結(jié)束
執(zhí)行timer3颜凯,console.log(5)同步任務(wù)谋币,直接執(zhí)行,打印5
檢查沒有微任務(wù)症概,第四次Event Loop結(jié)束
執(zhí)行timer1蕾额,console.log(2)同步任務(wù),直接執(zhí)行彼城,打印2
檢查沒有微任務(wù)凡简,也沒有宏任務(wù),第五次Event Loop結(jié)束
結(jié)果:1精肃,4秤涩,8,7司抱,3筐眷,6,5习柠,2

console.log('start')
setTimeout(function(){
  console.log('宏任務(wù)1號(hào)')
})
Promise.resolve().then(function(){
    console.log('微任務(wù)0號(hào)')
  })
console.log('執(zhí)行棧執(zhí)行中')
setTimeout(function(){
  console.log('宏任務(wù)2號(hào)')
  Promise.resolve().then(function(){
    console.log('微任務(wù)1號(hào)')
  })
},500)

setTimeout(function(){
  console.log('宏任務(wù)3號(hào)')
 setTimeout(function(){
  console.log('宏任務(wù)4號(hào)')
  Promise.resolve().then(function(){
    console.log('微任務(wù)2號(hào)')
  })
},500)
 Promise.resolve().then(function(){
    console.log('微任務(wù)3號(hào)')
  })
},600)
console.log('end')

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末匀谣,一起剝皮案震驚了整個(gè)濱河市照棋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌武翎,老刑警劉巖烈炭,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異宝恶,居然都是意外死亡符隙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門垫毙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來霹疫,“玉大人,你說我怎么就攤上這事综芥±鲂” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵膀藐,是天一觀的道長(zhǎng)屠阻。 經(jīng)常有香客問我,道長(zhǎng)额各,這世上最難降的妖魔是什么栏笆? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮臊泰,結(jié)果婚禮上蛉加,老公的妹妹穿的比我還像新娘。我一直安慰自己缸逃,他們只是感情好针饥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著需频,像睡著了一般丁眼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上昭殉,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天苞七,我揣著相機(jī)與錄音,去河邊找鬼挪丢。 笑死蹂风,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的乾蓬。 我是一名探鬼主播惠啄,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了撵渡?” 一聲冷哼從身側(cè)響起融柬,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎趋距,沒想到半個(gè)月后粒氧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡节腐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年外盯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铜跑。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡门怪,死狀恐怖骡澈,靈堂內(nèi)的尸體忽然破棺而出锅纺,到底是詐尸還是另有隱情,我是刑警寧澤肋殴,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布囤锉,位于F島的核電站,受9級(jí)特大地震影響护锤,放射性物質(zhì)發(fā)生泄漏官地。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一烙懦、第九天 我趴在偏房一處隱蔽的房頂上張望驱入。 院中可真熱鬧,春花似錦氯析、人聲如沸亏较。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽雪情。三九已至,卻和暖如春你辣,著一層夾襖步出監(jiān)牢的瞬間巡通,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工舍哄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宴凉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓表悬,卻偏偏與公主長(zhǎng)得像跪解,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容