JavaScript執(zhí)行機(jī)制

首先,看一道面試題

//請(qǐng)寫出輸出內(nèi)容
async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}

console.log('script start');

setTimeout(function() {
    console.log('setTimeout');
}, 0)

async1();

new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');
/*
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/

這道題目需要我們了解js的運(yùn)行機(jī)制

單線程

js的一大特點(diǎn)就是單線程讲婚,也就是說同一時(shí)間只能做一件事情。
js單線程的原因就是它是一個(gè)腳本語言够掠,主要用于用戶交互以及操作DOM洗显。如果是多線程搔耕,那么如果一個(gè)線程刪除了DOM阵幸,另一個(gè)線程又在操作這個(gè)DOM臊诊,那么應(yīng)該以哪個(gè)為準(zhǔn)呢癞埠?所以他的用途決定了它只能是單線程

任務(wù)隊(duì)列

單線程就表示所有的任務(wù)都要排隊(duì)状原,只有前面的任務(wù)執(zhí)行完畢之后才能執(zhí)行下一個(gè)任務(wù)聋呢。
有的時(shí)候因?yàn)閍jax請(qǐng)求等任務(wù)需要等待的時(shí)間很長,如果要等到請(qǐng)求完成在執(zhí)行下一個(gè)任務(wù)颠区,會(huì)浪費(fèi)大量時(shí)間削锰。此時(shí)js的設(shè)計(jì)者就想到了,可以將這些任務(wù)掛起毕莱,繼續(xù)執(zhí)行下面的任務(wù)器贩,等到請(qǐng)求有了結(jié)果再去執(zhí)行掛起的任務(wù)。
于是所有的任務(wù)可以分成同步任務(wù)和異步任務(wù)朋截。同步任務(wù)指的是在主線程上執(zhí)行蛹稍,只有當(dāng)前任務(wù)執(zhí)行完畢才能執(zhí)行下一個(gè)任務(wù)。異步任務(wù)是指不進(jìn)入主線程部服,而進(jìn)入"任務(wù)隊(duì)列"的任務(wù)唆姐。當(dāng)異步任務(wù)有了運(yùn)行結(jié)果,在可運(yùn)行的異步任務(wù)添加到執(zhí)行棧中
所以我們要明白以下幾點(diǎn):

  1. js分為同步任務(wù)和異步任務(wù)
  2. 同步任務(wù)都在主線程中形成廓八,形成一個(gè)執(zhí)行棧
  3. 異步任務(wù)會(huì)存放在任務(wù)隊(duì)列中奉芦,當(dāng)異步任務(wù)有了運(yùn)行結(jié)果,就放在任務(wù)隊(duì)列中放置一個(gè)事件
  4. 當(dāng)執(zhí)行棧中所有的同步任務(wù)執(zhí)行完畢之后剧蹂,就會(huì)去執(zhí)行任務(wù)隊(duì)列中放置的任務(wù)
  5. 以上過程會(huì)重復(fù)執(zhí)行

宏任務(wù)

(macro)task也就是宏任務(wù):我們可以認(rèn)為每次在執(zhí)行棧中執(zhí)行的代碼就是一個(gè)宏任務(wù)
(macro)task包括:script(整體代碼)声功、setTimeOut、setInterval宠叼、I/O减噪、UI交互事件、postMessage车吹、MessageChannel、setImmediate(Node.js環(huán)境)

微任務(wù)

microtask也就是微任務(wù):當(dāng)前task結(jié)束之后立即執(zhí)行的任務(wù)醋闭。這些任務(wù)響應(yīng)速度比setTimeout會(huì)更快窄驹,因?yàn)闊o需等待渲染。
microtask主要包括:Promise.then证逻、MutaionObserver乐埠、process.nextTick(Node.js環(huán)境)

事件循環(huán)

主線程從"任務(wù)隊(duì)列"中讀取事件,這個(gè)過程是循環(huán)不斷的囚企,所以整個(gè)的這個(gè)運(yùn)行機(jī)制叫事件循環(huán)
在事件循環(huán)中丈咐,每一次循環(huán)成為一個(gè)tick,每次tick任務(wù)處理模型比較復(fù)雜龙宏,但是關(guān)鍵步驟如下:

  • 執(zhí)行一個(gè)宏任務(wù)(棧中沒有就從事件隊(duì)列中獲取)
  • 執(zhí)行中遇到了微任務(wù)棵逊,就放到微任務(wù)隊(duì)列中
  • 宏任務(wù)執(zhí)行完畢之后,去微任務(wù)隊(duì)列中依次執(zhí)行所有的微任務(wù)
  • 當(dāng)前宏任務(wù)執(zhí)行完畢之后银酗,開始檢查渲染辆影,然后GUI線程接管渲染
  • 渲染完畢之后徒像,js線程繼續(xù)接管,開始下一個(gè)宏任務(wù)

Promise和async

Promise和async的異步體現(xiàn)在then和catch中蛙讥,所以寫在Promise和async中的代碼會(huì)立即執(zhí)行
async本身就是promise+generator的語法糖锯蛀。所以await后面的代碼就是microtask

async function async1() {
    console.log('async1 start');
    await async2();
}

等價(jià)于

async function async1() {
    console.log('async1 start');
    Promise.resolve(async2()).then(() => {
        console.log('async1 end');
    })
}

代碼分析

1.首先將整個(gè)代碼(script)壓如執(zhí)行棧


微信圖片_20190311100100.png

2.然后定義了兩個(gè)async方法,接著是一個(gè)console次慢,將console壓棧旁涤,然后輸出script start
微信圖片_20190311100535.png

3.接著遇到setTimeout,這是一個(gè)宏任務(wù)
微信圖片_20190311101036.png

4.接著執(zhí)行async1迫像,async1中的第一個(gè)console直接入棧劈愚,輸出async1 start,然后執(zhí)行await后面的async2侵蒙,輸出async2造虎,await之后的代碼就是一個(gè)微任務(wù)
微信圖片_20190311102424.png

5.下面遇到promise,promise的異步體現(xiàn)在then中纷闺,所以promise中的代碼立即壓棧算凿,執(zhí)行console,輸出promise1犁功,將then中的代碼放入到微任務(wù)中氓轰,然后執(zhí)行最后一句console,輸出script end
微信圖片_20190311103238.png

6.這時(shí)候第一宏任務(wù)已經(jīng)全部執(zhí)行完畢浸卦,此時(shí)我們執(zhí)行微任務(wù)隊(duì)列中的兩個(gè)console署鸡,依次輸出async1 end 和promise2
微信圖片_20190311103708.png

7.然后執(zhí)行下一個(gè)宏任務(wù),也就是setTimeout,輸出setTimeout
微信圖片_20190311103908.png

參考文獻(xiàn):
1.https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/7
2.http://www.ruanyifeng.com/blog/2014/10/event-loop.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末限嫌,一起剝皮案震驚了整個(gè)濱河市靴庆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌怒医,老刑警劉巖炉抒,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異稚叹,居然都是意外死亡焰薄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門扒袖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來塞茅,“玉大人,你說我怎么就攤上這事季率∫笆荩” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵飒泻,是天一觀的道長缅刽。 經(jīng)常有香客問我啊掏,道長,這世上最難降的妖魔是什么衰猛? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任迟蜜,我火速辦了婚禮,結(jié)果婚禮上啡省,老公的妹妹穿的比我還像新娘娜睛。我一直安慰自己,他們只是感情好卦睹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布畦戒。 她就那樣靜靜地躺著,像睡著了一般结序。 火紅的嫁衣襯著肌膚如雪障斋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天徐鹤,我揣著相機(jī)與錄音垃环,去河邊找鬼。 笑死返敬,一個(gè)胖子當(dāng)著我的面吹牛遂庄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播劲赠,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼涛目,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了凛澎?” 一聲冷哼從身側(cè)響起霹肝,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎塑煎,沒想到半個(gè)月后沫换,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡轧叽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了刊棕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炭晒。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖甥角,靈堂內(nèi)的尸體忽然破棺而出网严,到底是詐尸還是另有隱情,我是刑警寧澤嗤无,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布震束,位于F島的核電站怜庸,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏垢村。R本人自食惡果不足惜割疾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嘉栓。 院中可真熱鬧宏榕,春花似錦、人聲如沸侵佃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽馋辈。三九已至抚芦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間迈螟,已是汗流浹背叉抡。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留井联,地道東北人卜壕。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像烙常,于是被迫代替她去往敵國和親轴捎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355