實(shí)例分析js事件循環(huán)機(jī)制
本文通過實(shí)例給大家詳細(xì)分析了JS中事件循環(huán)機(jī)制的原理和用法,以下是全部內(nèi)容:
var start = new Date() setTimeout(function () { var end = new Date console.log('Time elapsed:', end - start, 'ms') }, 500) while (new Date() - start < 1000) { }
有其他語言能完成預(yù)期的功能嗎?Java, 在Java.util.Timer中,對于定時(shí)任務(wù)的解決方案是通過多線程手段實(shí)現(xiàn)的,任務(wù)對象存儲在任務(wù)隊(duì)列,由專門的調(diào)度線程,在新的子線程中完成任務(wù)的執(zhí)行
js是單線程的
JavaScript的主要用途是與用戶互動,以及操作DOM。這決定了它只能是單線程,否則會帶來很復(fù)雜的同步問題。
為了利用多核CPU的計(jì)算能力,HTML5提出Web Worker標(biāo)準(zhǔn),允許JavaScript腳本創(chuàng)建多個(gè)線程,但是子線程完全受主線程控制,且不得操作DOM。所以,這個(gè)新標(biāo)準(zhǔn)并沒有改變JavaScript單線程的本質(zhì)。
函數(shù)調(diào)用棧和任務(wù)隊(duì)列
調(diào)用棧
JS執(zhí)行時(shí)會形成調(diào)用棧,調(diào)用一個(gè)函數(shù)時(shí),返回地址、參數(shù)、本地變量都會被推入棧中,如果當(dāng)前正在運(yùn)行的函數(shù)中調(diào)用另外一個(gè)函數(shù),則該函數(shù)相關(guān)內(nèi)容也會被推入棧頂.該函數(shù)執(zhí)行完畢,則會被彈出調(diào)用棧.變量也隨之彈出,由于復(fù)雜類型值存放于堆中,因此彈出的只是指針,他們的值依然在堆中,由GC決定回收.
事件循環(huán)(event loop) & 任務(wù)隊(duì)列(task queue)
JavaScript 主線程擁有一個(gè)執(zhí)行棧以及一個(gè)任務(wù)隊(duì)列
遇到異步操作(例如:setTimeout, AJAX)時(shí),異步操作會由瀏覽器(OS)執(zhí)行,瀏覽器會在這些任務(wù)完成后,將事先定義的回調(diào)函數(shù)推入主線程的任務(wù)隊(duì)列(task queue)中,當(dāng)主線程的執(zhí)行棧清空之后會讀取task queue中的回調(diào)函數(shù),當(dāng)task queue被讀取完畢之后,主線程接著執(zhí)行,從而進(jìn)入一個(gè)無限的循環(huán),這就是事件循環(huán).
主線程執(zhí)行棧 & 任務(wù)隊(duì)列 循環(huán)執(zhí)行,構(gòu)成事件循環(huán)
結(jié)論
setTimeout()只是將事件插入了"任務(wù)隊(duì)列",必須等到當(dāng)前代碼(執(zhí)行棧)執(zhí)行完,主線程才會去執(zhí)行它指定的回調(diào)函數(shù)。要是當(dāng)前代碼耗時(shí)很長,有可能要等很久,所以并沒有辦法保證,回調(diào)函數(shù)一定會在setTimeout()指定的時(shí)間執(zhí)行。
另一個(gè)例子
(function test() { setTimeout(function() {console.log(4)}, 0); new Promise(function executor(resolve) { console.log(1); for( var i=0 ; i<10000 ; i++ ) { i == 9999 && resolve(); } console.log(2); }).then(function() { console.log(5); }); console.log(3); })()
Macrotask & Microtask
macrotask 和 microtask 是異步任務(wù)的兩種分類。在掛起任務(wù)時(shí),JS 引擎會將所有任務(wù)按照類別分到這兩個(gè)隊(duì)列中,首先在 macrotask 的隊(duì)列(這個(gè)隊(duì)列也被叫做 task queue)中取出第一個(gè)任務(wù),執(zhí)行完畢后取出 microtask 隊(duì)列中的所有任務(wù)順序執(zhí)行;之后再取 macrotask 任務(wù),周而復(fù)始,直至兩個(gè)隊(duì)列的任務(wù)都取完。
macro-task: script(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering
micro-task: process.nextTick, Promises(這里指瀏覽器實(shí)現(xiàn)的原生 Promise), Object.observe, MutationObserver
結(jié)論
全部代碼(script) macrotask -> microtask queue (含有promise.then) -> macrotask(setTimeout) -> 下一個(gè)microtask
Node.js的事件循環(huán)
process.nextTick & setImmediate
process.nextTick指定的任務(wù)總是發(fā)生在所有異步任務(wù)之前
setImmediate指定的任務(wù)總是在下一次Event Loop時(shí)執(zhí)行
process.nextTick(function A() { console.log(1); process.nextTick(function B(){console.log(2);}); }); setTimeout(function timeout() { console.log('TIMEOUT FIRED'); }, 0)
new Promise(function(resolve) { console.log('glob1_promise'); resolve(); }).then(function() { console.log('glob1_then') }) process.nextTick(function() { console.log('glob1_nextTick'); })
相關(guān)文章
JavaScript數(shù)組及常見操作方法小結(jié)
這篇文章主要介紹了JavaScript數(shù)組及常見操作方法,結(jié)合實(shí)例形式總結(jié)分析了JavaScript數(shù)組的基本獲取、添加、刪除、排序、翻轉(zhuǎn)等相關(guān)操作技巧,需要的朋友可以參考下2019-11-11JavaScript ECMA-262-3 深入解析(二):變量對象實(shí)例詳解
這篇文章主要介紹了JavaScript ECMA-262-3變量對象,結(jié)合實(shí)例形式詳細(xì)分析了JavaScript ECMA變量對象相關(guān)概念、原理、用法及操作注意事項(xiàng),需要的朋友可以參考下2020-04-04前端處理二進(jìn)制流文件導(dǎo)出為excel表代碼示例
這篇文章主要給大家介紹了關(guān)于前端處理二進(jìn)制流文件導(dǎo)出為excel表的相關(guān)資料,后臺管理系統(tǒng),常會出現(xiàn)導(dǎo)出excel表格功能,需要的朋友可以參考下2023-08-08js刪除數(shù)組元素、清空數(shù)組的簡單方法(必看)
下面小編就為大家?guī)硪黄猨s刪除數(shù)組元素、清空數(shù)組的簡單方法(必看)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-07-07js 發(fā)個(gè)判斷字符串是否為符合標(biāo)準(zhǔn)的函數(shù)
判斷字符是不是ip和是不是數(shù)字的函數(shù)。2009-04-04理解javascript定時(shí)器中的setTimeout與setInterval
這篇文章主要幫助大家學(xué)習(xí)理解javascript定時(shí)器中的setTimeout與setInterval,從實(shí)例出發(fā)進(jìn)行深入探討,感興趣的小伙伴們可以參考一下2016-02-02