js的事件循環(huán)

Event Loop

前言

還記得那些年面試官問你的定時器的原理嗎?還有呢?Promise的原理呢琼讽?原理必峰、原理、原理钻蹬,問的我們懷疑人生吼蚁。

為了下次不再懵逼,今天问欠,我們來了解一下Event Loop的概念肝匆。我們的初衷是真正的了解和掌握它,了解整體JavaScript的運行機制溅潜。至少术唬,我們在看完文章的時候,不會讓我們在懷疑人生了滚澜。如果你喜歡我的文章粗仓,歡迎評論,歡迎Star~

正文

聊這一個話題设捐,我們必須從JavaScript本身聊起借浊。

為什么瀏覽器選擇了我

眾所周知,JavaScript是一門單線程語言萝招。為什么會在瀏覽器端開發(fā)一門這樣子的單線程原因呢蚂斤?原因就是——簡單。在瀏覽器端槐沼,復雜的UI環(huán)境會限制多線程語言的開發(fā)曙蒸。例如,一個線程在操作一個DOM元素時岗钩,另一個線程需要去刪除DOM元素纽窟,這個之間就需要進行狀態(tài)的同步,何況前端可能不止操作這么一個DOM元素兼吓。所以臂港,為了避免在開發(fā)過程中,去進行復雜的同步视搏,選用單線程語言進行開發(fā)是最好的解決方案审孽。

但是浑娜,單線程語言也會有問題——同步阻塞佑力。舉個例子,cpu在執(zhí)行程序的過程中筋遭,執(zhí)行到IO操作,它會發(fā)送IO請求,之后等待數(shù)據(jù)返回养距,待數(shù)據(jù)返回之后才會繼續(xù)執(zhí)行后面的任務,如圖束析。


image

這種問題在瀏覽器中恶守,或許是致命的利花。因此,JavaScript在執(zhí)行過程中,將任務分為同步任務和異步任務。

同步和異步的我

首先娩贷,我們來了解一下主線程的執(zhí)行棧(call stack)。一個線程對應著一個執(zhí)行棧储笑,所有同步任務都會被放入到執(zhí)行棧中執(zhí)行秕磷,例如贩汉,我們有下面這樣子的一個程序:

function fun1(){
  return 'hello hip-hop';
}

function fun2(){
  return fun1();
}

function fun3(){
  console.log(fun2());
}

fun3();   //'hello hip-hop'

調用棧執(zhí)行過程:

call stack1

或者我們使用另一種方法來論證一下:我們將fun1改成throw new Error('hello hip-hop')

function fun1(){
  throw new Error('hello hip-hop');
}

function fun2(){
  return fun1();
}

function fun3(){
  console.log(fun2());
}

fun3();
call stack2

每次調用過程中,會有函數(shù)的出棧和入棧锚赤。這就是同步任務執(zhí)行的過程匹舞,一個任務執(zhí)行完成之后,執(zhí)行下一個任務线脚。那么赐稽,我們在來看一下另外一個例子:

console.log('first');
setTimeout(() => {
  console.log('second');
}, 500);
console.log('three');

我們依舊來看一下這個程序的調用棧的執(zhí)行順序:

call stack3

從圖中,我們可以看到setTimeout執(zhí)行完成之后浑侥,已經(jīng)出棧了姊舵,但是后來的console.log('second')又是如何入棧的呢?

其實寓落,在主線程之外括丁,還有一個任務隊列。在任務隊列中伶选,會存放著異步任務史飞,只有當指定事件發(fā)生之后,異步任務才會被放到主線程中執(zhí)行仰税。

其實构资,任務隊列中是一個事件隊列,那setTimeout舉例來說陨簇,當主線程執(zhí)行setTimeout的時候吐绵,會創(chuàng)建一個定時器,一旦定時器的時間達到了河绽,就會將其內部的回調函數(shù)己单,放入任務隊列中。然后葵姥,主線程在執(zhí)行的最后去循環(huán)任務隊列荷鼠。而回調函數(shù),指的是主線程掛起的代碼榔幸,異步任務必須有回調函數(shù)才能執(zhí)行接下來的操作允乐。如圖:

event loop

事件循環(huán)

其實矮嫉,上圖中,我們可以看到一個循環(huán)牍疏,如圖:

event loop

這是一個死循環(huán)蠢笋,無論哪種情況都是閉環(huán)。其實鳞陨,這就是事件循環(huán)昨寞。事件循環(huán)會不斷地去檢測任務隊列中是否還有已觸發(fā)時間的任務,如果有的話厦滤,就放入主線程中執(zhí)行援岩。但是事件循環(huán)一般會在主線程中任務執(zhí)行完成之后執(zhí)行。我們可以來看一下另一幅圖片:

概覽

這幅圖片中掏导,我們可以看到完整的執(zhí)行流程享怀,其中涉及到的異步事件有DOM事件、ajax請求和setTimeout趟咆。所以添瓷,整體的執(zhí)行流程是這樣子的:

(1)、所有同步任務會在主線程的調用棧中執(zhí)行值纱。
(2)鳞贷、在主線程之外,還有一個任務隊列虐唠,一旦指定事件發(fā)生之后搀愧,異步任務就會被放入任務隊列中
(3)、當主線程執(zhí)行完調用棧中的同步任務時疆偿,會遍歷任務隊列妈橄,將任務隊列中的任務放入主線程中執(zhí)行。而之后事件循環(huán)一直會去遍歷任務隊列翁脆,一旦有任務放入就會放入主線程中執(zhí)行眷蚓。

這樣,我們就已經(jīng)初步了解了同步和異步之間的實現(xiàn)反番,以及瀏覽器中的事件循環(huán)機制沙热。

任務隊列的不同

自從ES6標準出來之后,Promise就被開發(fā)者關注到了罢缸。Promise是個很有意思的家伙篙贸,詳細講解的話,篇幅會太長枫疆。我們只關注它異步方面的任務隊列爵川。

首先,我們來看一段程序:

setTimeout(() => {
  console.log(1);
}, 0);

Promise.resolve().then(() => {
  console.log(2);
}).then(() => {
  console.log(3);
});

console.log(4);      // 4   2    3    1

你會對它的輸出感到疑惑嗎息楔?相信經(jīng)歷過面試的你一定會寝贡?還是兩個字——懵逼

其實扒披,這個執(zhí)行順序和任務隊列的種類有關系。我們一般一直稱呼地任務隊列(task queue)圃泡,其實指的是Macrotasks碟案。而Promise執(zhí)行后會被放到Microtasks中。

Macrotasks => 一般會將dom事件颇蜡、ajax事件和setTimeout放入到這個隊列中价说。

Microtasks => 一般會將Promise、process.nextTicks风秤、MutationObserver放入這個隊列中鳖目。

在執(zhí)行事件循環(huán)時,主線程會首先遍歷Microtasks缤弦,然后將隊列中的異步任務抽取出來執(zhí)行疑苔,直至抽空整個隊列,才會去執(zhí)行Macrotasks的隊列中的異步任務甸鸟。

所以,上面函數(shù)的調用棧過程如下:

call stack4

總結

js的事件循環(huán)部分兵迅,內容應該算是全部闡述完全了抢韭。希望對看的你,會有收獲恍箭。

本文來自我的github刻恭,原文地址
如果你對我寫的有疑問是越,可以評論姑食,如我寫的有錯誤猿棉,歡迎指正满葛。你喜歡我的博客蜂林,請給我關注Star~呦

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末送粱,一起剝皮案震驚了整個濱河市似谁,隨后出現(xiàn)的幾起案子哼蛆,更是在濱河造成了極大的恐慌构拳,老刑警劉巖咆爽,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異置森,居然都是意外死亡斗埂,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門凫海,熙熙樓的掌柜王于貴愁眉苦臉地迎上來呛凶,“玉大人,你說我怎么就攤上這事行贪⊙。” “怎么了模闲?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長县好。 經(jīng)常有香客問我围橡,道長,這世上最難降的妖魔是什么缕贡? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任翁授,我火速辦了婚禮,結果婚禮上晾咪,老公的妹妹穿的比我還像新娘收擦。我一直安慰自己,他們只是感情好谍倦,可當我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布塞赂。 她就那樣靜靜地躺著,像睡著了一般昼蛀。 火紅的嫁衣襯著肌膚如雪宴猾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天叼旋,我揣著相機與錄音仇哆,去河邊找鬼。 笑死夫植,一個胖子當著我的面吹牛讹剔,可吹牛的內容都是我干的。 我是一名探鬼主播详民,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼延欠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了沈跨?” 一聲冷哼從身側響起由捎,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎饿凛,沒想到半個月后隅俘,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡笤喳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年为居,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杀狡。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蒙畴,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情膳凝,我是刑警寧澤碑隆,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站蹬音,受9級特大地震影響上煤,放射性物質發(fā)生泄漏。R本人自食惡果不足惜著淆,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一劫狠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧永部,春花似錦独泞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至组橄,卻和暖如春荞膘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背玉工。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工羽资, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瓮栗。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像瞄勾,于是被迫代替她去往敵國和親费奸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內容