事件循環(huán)EventLoop機制

1. 宏任務(wù)與微任務(wù)

依據(jù)我們多年編寫 ajax 的經(jīng)驗:js 應(yīng)該是按照語句先后順序執(zhí)行,在出現(xiàn)異步時,則發(fā)起異步請求后尖奔,接著往下執(zhí)行茬底,待異步結(jié)果返回后再接著執(zhí)行沪悲。但他內(nèi)部是怎樣管理這些執(zhí)行任務(wù)的呢?

在 js 中阱表,任務(wù)分為宏任務(wù)(macrotask)和微任務(wù)(microtask)殿如,這兩個任務(wù)分別維護一個隊列贡珊,均采用先進先出的策略進行執(zhí)行!同步執(zhí)行的任務(wù)都在宏任務(wù)上執(zhí)行涉馁。

宏任務(wù)主要有:script(整體代碼)门岔、setTimeout、setInterval烤送、I/O寒随、UI 交互事件、postMessage帮坚、MessageChannel妻往、setImmediate(Node.js 環(huán)境)。

微任務(wù)主要有:Promise.then叶沛、 MutationObserver蒲讯、 process.nextTick(Node.js 環(huán)境)。

具體的操作步驟如下:

  1. 從宏任務(wù)的頭部取出一個任務(wù)執(zhí)行灰署;
  2. 執(zhí)行過程中若遇到微任務(wù)則將其添加到微任務(wù)的隊列中判帮;
  3. 宏任務(wù)執(zhí)行完畢后,微任務(wù)的隊列中是否存在任務(wù)溉箕,若存在晦墙,則挨個兒出去執(zhí)行,直到執(zhí)行完畢肴茄;
  4. GUI 渲染晌畅;
  5. 回到步驟 1,直到宏任務(wù)執(zhí)行完畢寡痰;

這 4 步構(gòu)成了一個事件的循環(huán)檢測機制抗楔,即我們所稱的eventloop。

例子:

console.log(1);
setTimeout(function() {
    console.log(2);
}, 0);
new Promise(function(resolve) {
    console.log(3);
    resolve(Date.now());
}).then(function() {
    console.log(4);
});
console.log(5);
setTimeout(function() {
    new Promise(function(resolve) {
        console.log(6);
        resolve(Date.now());
    }).then(function() {
        console.log(7);
    });
}, 0);
  1. 執(zhí)行 log(1)拦坠,輸出 1连躏;
  2. 遇到 setTimeout,將回調(diào)的代碼 log(2)添加到宏任務(wù)中等待執(zhí)行贞滨;
  3. 執(zhí)行 console.log(3)入热,將 then 中的 log(4)添加到微任務(wù)中;
  4. 執(zhí)行 log(5)晓铆,輸出 5勺良;
  5. 遇到 setTimeout,將回調(diào)的代碼 log(6, 7)添加到宏任務(wù)中骄噪;
  6. 宏任務(wù)的一個任務(wù)執(zhí)行完畢尚困,查看微任務(wù)隊列中是否存在任務(wù),存在一個微任務(wù) log(4)(在步驟 3 中添加的)链蕊,執(zhí)行輸出 4尾组;
  7. 取出下一個宏任務(wù) log(2)執(zhí)行忙芒,輸出 2;
  8. 宏任務(wù)的一個任務(wù)執(zhí)行完畢讳侨,查看微任務(wù)隊列中是否存在任務(wù)呵萨,不存在;
  9. 取出下一個宏任務(wù)執(zhí)行跨跨,執(zhí)行 log(6)潮峦,將 then 中的 log(7)添加到微任務(wù)中;
  10. 宏任務(wù)執(zhí)行完畢勇婴,存在一個微任務(wù) log(7)(在步驟 9 中添加的)忱嘹,執(zhí)行輸出 7;

因此耕渴,最終的輸出順序為:1, 3, 5, 4, 2, 6, 7;

我們在Promise.then實現(xiàn)一個稍微耗時的操作拘悦,這個步驟看起來會更加地明顯:

console.log(1);
var start = Date.now();
setTimeout(function() {
    console.log(2);
}, 0);
setTimeout(function() {
    console.log(4, Date.now() - start);
}, 400);
Promise.resolve().then(function() {
    var sum = function(a, b) {
        return Number(a) + Number(b);
    }
    var res = [];
    for(var i=0; i<5000000; i++) {
        var a = Math.floor(Math.random()*100);
        var b = Math.floor(Math.random()*200);
        res.push(sum(a, b));
    }
    res = res.sort();
    console.log(3);
})

Promise.then中,先生成一個500萬隨機數(shù)的數(shù)組橱脸,然后對這個數(shù)組進行排序础米。運行這段代碼可以發(fā)現(xiàn):馬上會輸出1,稍等一會兒才會輸出3添诉,然后再輸出2屁桑。不論等待多長時間輸出3,2一定會在3的后面輸出栏赴。這也就印證了eventloop中的第3步操作蘑斧,必須等所有的微任務(wù)執(zhí)行完畢后,才開始下一個宏任務(wù)须眷。

本來要設(shè)定的是400ms后輸出竖瘾,但因為之前的任務(wù)耗時嚴重,導致之后的任務(wù)只能延遲往后排花颗。也能說明捕传,setTimeout和setInterval這種操作的延時是不準確的,這兩個方法只能大概將任務(wù)400ms之后的宏任務(wù)中捎稚,但具體的執(zhí)行時間乐横,還是要看線程是否空閑求橄。 **若前一個任務(wù)中有耗時的操作今野,或者有無限的微任務(wù)加入進來時,則會阻塞下一個任務(wù)的執(zhí)行****

2. async-await

從上面的代碼中也能看到 Promise.then 中的代碼是屬于微服務(wù)罐农,那么 async-await 的代碼怎么執(zhí)行呢条霜?比如下面的代碼:

function A() {
    return Promise.resolve(Date.now());
}
async function B() {
    console.log(Math.random());
    let now = await A();
    console.log(now);
}
console.log(1);
B();
console.log(2);

其實,async-await 只是 Promise+generator 的一種語法糖而已涵亏。上面的代碼我們改寫為這樣宰睡,可以更加清晰一點:

function B() {
    console.log(Math.random());
    A().then(function(now) {
        console.log(now);
    })
}
console.log(1);
B();
console.log(2);

這樣我們就能明白輸出的先后順序了: 1, 0.4793526730678652(隨機數(shù)), 2, 1557830834679(時間戳);

3. requestAnimationFrame

requestAnimationFrame也屬于異步執(zhí)行的方法蒲凶,但該方法既不屬于宏任務(wù),也不屬于微任務(wù)拆内。

window.requestAnimationFrame() 告訴瀏覽器——你希望執(zhí)行一個動畫旋圆,并且要求瀏覽器在下次重繪之前調(diào)用指定的回調(diào)函數(shù)更新動畫。該方法需要傳入一個回調(diào)函數(shù)作為參數(shù)麸恍,該回調(diào)函數(shù)會在瀏覽器下一次重繪之前執(zhí)行

requestAnimationFrame是GUI渲染之前執(zhí)行灵巧,但在微服務(wù)之后,不過requestAnimationFrame不一定會在當前幀必須執(zhí)行抹沪,由瀏覽器根據(jù)當前的策略自行決定在哪一幀執(zhí)行刻肄。

4. 總結(jié)

我們要記住最重要的兩點:js是單線程和eventloop的循環(huán)機制。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末融欧,一起剝皮案震驚了整個濱河市敏弃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌噪馏,老刑警劉巖麦到,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異逝薪,居然都是意外死亡隅要,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門董济,熙熙樓的掌柜王于貴愁眉苦臉地迎上來步清,“玉大人,你說我怎么就攤上這事虏肾±。” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵封豪,是天一觀的道長谴轮。 經(jīng)常有香客問我,道長吹埠,這世上最難降的妖魔是什么第步? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮缘琅,結(jié)果婚禮上粘都,老公的妹妹穿的比我還像新娘。我一直安慰自己刷袍,他們只是感情好翩隧,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著呻纹,像睡著了一般堆生。 火紅的嫁衣襯著肌膚如雪专缠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天淑仆,我揣著相機與錄音涝婉,去河邊找鬼。 笑死蔗怠,一個胖子當著我的面吹牛嘁圈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蟀淮,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼最住,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了怠惶?” 一聲冷哼從身側(cè)響起涨缚,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎策治,沒想到半個月后脓魏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡通惫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年茂翔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片履腋。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡珊燎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出遵湖,到底是詐尸還是另有隱情悔政,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布延旧,位于F島的核電站谋国,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏迁沫。R本人自食惡果不足惜芦瘾,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望集畅。 院中可真熱鬧近弟,春花似錦、人聲如沸牡整。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逃贝。三九已至谣辞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間沐扳,已是汗流浹背泥从。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沪摄,地道東北人躯嫉。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像杨拐,于是被迫代替她去往敵國和親祈餐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

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

  • 小事兒就是大事情 女兒的“隱私” 佳句兒童并非整天無憂無慮哄陶,他們經(jīng)常會有自己的心思和困惑帆阳,甚至痛苦和悲傷,家長要善...
    huangli閱讀 2,603評論 0 0
  • 今天完成事項:1.政協(xié)分提案屋吨,重點提案督辦蜒谤;2.盡量減少省政府辦公廳提案。 明天預辦事項:1.人大建議修改和辦文至扰;...
    小夏要上進閱讀 202評論 0 0
  • 簡介 : 《復仇者聯(lián)盟4:終局之戰(zhàn)》:漫威影業(yè)榮譽出品《復仇者聯(lián)盟4:終局之戰(zhàn)》敢课,故事發(fā)生在滅霸消滅宇宙一半的生靈...
    世紀影眼閱讀 379評論 0 0