詳解事件循環(huán)與任務隊列

本文首發(fā)于我的博客懂算,這是我的github,歡迎star穗慕。

在網(wǎng)上找了很多事件循環(huán)和任務隊列相關的文章,有些說的不是很清楚妻导,看完感覺還是有些暈暈乎乎逛绵,所以寫這篇博客把整體思路梳理一下。如果你有什么不同的理解倔韭,或是疑惑的地方术浪,可以留言討論。這里是我的github寿酌,歡迎來訪胰苏。

事件循環(huán)與任務隊列是JS中比較重要的兩個概念。這兩個概念在ES5ES6兩個標準中有不同的實現(xiàn)醇疼。尤其在ES6標準中碟联,清楚的區(qū)分宏觀任務隊列微觀任務隊列才能解釋Promise一些看似奇怪的表現(xiàn)妓美。

事件循環(huán)

事件循環(huán)是什么?為什么要有事件循環(huán)這個東西鲤孵?我們都知道JS是單線程的壶栋,但是像Ajax,或是DOM事件這種很耗時的操作普监,需要用并發(fā)處理贵试,否則單線程會長時間等待,什么也做不了凯正。而單線程循環(huán)就是并發(fā)的一種形式毙玻,一個線程中只有一個事件循環(huán)。而任務隊列是用來配合事件循環(huán)完成操作的廊散,一個線程可以擁有多個任務隊列桑滩。

任務隊列

任務隊列是什么?故名思意允睹,排著任務的隊列运准。所謂任務是WebAPIs返回的一個個通知,讓JS主線程在讀取任務隊列的時候得知這個異步任務已經(jīng)完成缭受,下一步該執(zhí)行這個任務的回調(diào)函數(shù)了胁澳。主線程擁有多個任務隊列,不同的任務隊列用來排列來自不同任務源的任務米者。任務源是什么韭畸?像setTimeout/Promise/DOM事件等都是任務源,來自同類任務源的任務我們稱它們是同源的蔓搞,比如setTimeout與setInterval就是同源的胰丁。在ES6標準中任務隊列又分為宏觀任務隊列微觀任務隊列,我們后邊再詳細討論喂分。

下面先通俗的講述一下ES5中事件循環(huán)到底是怎么循環(huán)的隘马,如圖(據(jù)阮一峰前輩的教程):

事件循環(huán)1

圖中有三大塊:

  • 函數(shù)調(diào)用棧:即執(zhí)行棧。
  • WebAPIs:瀏覽器的接口妻顶。比如一個Ajax操作酸员,主線程會把收發(fā)Ajax交給瀏覽器的API,之后就繼續(xù)做別的事情讳嘱,瀏覽器在接收到Ajax返回的數(shù)據(jù)之后幔嗦,會把一個Ajax完成的事件排到相應的任務隊列后邊。
  • 任務隊列們:主線程中有多個任務隊列沥潭,同源的任務排在屬于自己的任務隊列邀泉。

一個具體點的栗子。比如現(xiàn)在打開了一個頁面,里邊有一段<script>汇恤,其中有Ajax庞钢,DOM操作等等。這段JS是在瀏覽器提供的全局環(huán)境(瀏覽器中是window)里執(zhí)行的因谎,執(zhí)行中遇到函數(shù)調(diào)用時會壓入執(zhí)行棧基括。

  1. 主線程在遇到Ajax或是setTimeout這種異步操作時會交給瀏覽器的WebAPIs,然后繼續(xù)執(zhí)行后邊的代碼财岔,直到最后執(zhí)行棧為空风皿。
  2. 瀏覽器會在不確定的時間將完成的任務返回,排到相應的任務隊列后匠璧。
  3. 執(zhí)行棧為空后桐款,主線程會到任務隊列中去取任務,這些任務會告訴下一步應該執(zhí)行哪些回調(diào)函數(shù)夷恍。任務隊列是具有優(yōu)先級的魔眨,按照優(yōu)先級決定訪問的先后順序。而優(yōu)先級在不同的環(huán)境中會有所不同酿雪,所以不能給出一個固定的優(yōu)先級遏暴。
  4. 每訪問一個隊列,執(zhí)行棧會執(zhí)行完這個任務隊列的所有的代碼执虹,然后再取下一個任務隊列需要執(zhí)行的的代碼拓挥。如果在執(zhí)行中遇到了當前屬于任務隊列的異步任務時唠梨。此次任務的返回不會直接排到當前任務隊列之后袋励。因為這屬于兩次不同的事件循環(huán),會被區(qū)分開來当叭。

就這樣循環(huán)執(zhí)行茬故,直到三大塊全為空,這稱為事件循環(huán)蚁鳖。

微觀任務隊列

ES6標準中任務隊列存在兩種類型磺芭,一種就是上邊提到的一些隊列,比如setTimeout醉箕、網(wǎng)絡請求Ajax钾腺、用戶I\O等都屬于宏觀任務隊列(macrotask queue),另一種是微觀任務隊列(microtask queue)讥裤,Promise就屬于微觀任務隊列放棒。
??添加了微觀任務隊列之后事件循環(huán)有什么變化呢?在執(zhí)行棧執(zhí)行的過程中會把屬于微觀任務隊列的任務分配到相應的微觀任務隊列中去己英。而在調(diào)用棧執(zhí)行空之后间螟,主線程讀取任務隊列時,會先讀取所有微觀任務隊列,然后讀取一個宏觀任務隊列厢破,再讀取所有的微觀任務隊列荣瑟。如圖:

事件循環(huán)2

好了,說了很多概念上的東西摩泪,不如一段代碼來的清晰:

setTimeout(function(){console.log(4)},0);
new Promise(function(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);
  • 腳本開始執(zhí)行笆焰,最先遇到setTimeout,交給瀏覽器去計時加勤,達到setTimeout限制最短計時之后仙辟,把這個任務推入setTimeout隊列。
  • 遇到Promise構(gòu)造函數(shù)鳄梅,構(gòu)造函數(shù)參數(shù)執(zhí)行叠国,輸出1,調(diào)用resolve改變Promise對象的狀態(tài)戴尸,然后輸出2粟焊。
  • Promise對象調(diào)用then方法,將這個任務推入Promise任務隊列孙蒙。
  • 執(zhí)行console.log(3)项棠,輸出3
  • 調(diào)用棧為空挎峦,讀取任務隊列香追,按照
    讀取所有微觀任務隊列 -> 執(zhí)行 ->
    讀取一個宏觀任務隊列 -> 執(zhí)行 ->
    讀取所有微觀任務隊列 -> 執(zhí)行 ->
    再讀取一個宏觀任務隊列...的順序。
  • 讀取所有微觀任務隊列中的任務坦胶,執(zhí)行這些任務指定的回調(diào)函數(shù)透典。執(zhí)行then指定的回調(diào)函數(shù),輸出5(微觀任務隊列也具有優(yōu)先級)顿苇。
  • 最后讀取到setTimeout的任務峭咒,執(zhí)行回調(diào)函數(shù),輸出4纪岁。

所以最后的輸出順序是1,2,3,5,4凑队,而不是1,2,3,4,5。如果不清楚微觀任務隊列的執(zhí)行機制幔翰,很容易將兩個異步任務歸為一類漩氨,將執(zhí)行順序判斷錯誤。

到這里算是把事件循環(huán)和任務隊列說的比較清楚了遗增,參考了很多大佬的博客與討論:
http://www.ruanyifeng.com/blog/2014/10/event-loop.html
https://www.zhihu.com/question/36972010
http://www.reibang.com/p/12b9f73c5a4f
http://www.cnblogs.com/hity-tt/p/6733062.html

如果你有不同的理解請到博客下方留言叫惊,這是我的github,歡迎來訪贡定,你的star就是我的動力赋访。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蚓耽,更是在濱河造成了極大的恐慌渠牲,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件步悠,死亡現(xiàn)場離奇詭異签杈,居然都是意外死亡,警方通過查閱死者的電腦和手機鼎兽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門答姥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谚咬,你說我怎么就攤上這事鹦付。” “怎么了择卦?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵敲长,是天一觀的道長。 經(jīng)常有香客問我秉继,道長祈噪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任尚辑,我火速辦了婚禮辑鲤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘杠茬。我一直安慰自己月褥,他們只是感情好,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布澈蝙。 她就那樣靜靜地躺著吓坚,像睡著了一般撵幽。 火紅的嫁衣襯著肌膚如雪灯荧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天盐杂,我揣著相機與錄音逗载,去河邊找鬼。 笑死链烈,一個胖子當著我的面吹牛厉斟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播强衡,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼擦秽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起感挥,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤缩搅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后触幼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體硼瓣,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年置谦,在試婚紗的時候發(fā)現(xiàn)自己被綠了堂鲤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡媒峡,死狀恐怖瘟栖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情谅阿,我是刑警寧澤慢宗,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站奔穿,受9級特大地震影響镜沽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贱田,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一缅茉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧男摧,春花似錦蔬墩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至乔询,卻和暖如春樟插,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背竿刁。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工黄锤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人食拜。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓鸵熟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親负甸。 傳聞我的和親對象是個殘疾皇子流强,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

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