JavaScript 運行機制 & EventLoop

JavaScript 運行機制 & EventLoop

阮老師博客和自己的理解宴杀,記錄的學習筆記,js的單線程和 事件EventLoop 機制购桑。

1. JavaScript是單線程

JavaScript語言的一大特點就是單線程,也就是說,同一個時間只能做一件事屏积。

js這門語言在剛開始創(chuàng)造時,就是作為瀏覽器腳本語言伸但,只要用途是與用戶互動肾请,操作dom,這決定了它只能為單線程更胖,單線程已成為JavaScript這門語言的核心特征铛铁,將來也不會改變。

HTML5提出Web Worker標準却妨,允許JavaScript腳本創(chuàng)建多個線程饵逐,但是子線程完全受主線程控制,且不得操作DOM彪标。所以倍权,這個新標準并沒有改變JavaScript單線程的本質(zhì)

2. 任務隊列

單線程就意味著,所有任務需要排隊捞烟,前一個任務結束薄声,才會執(zhí)行后一個任務。如果前一個任務耗時很長题画,后一個任務就不得不一直等著默辨,導致很多時候CPU都是空閑著的。

JavaScript語言的設計者意識到苍息,這時主線程完全可以不管IO設備缩幸,掛起處于等待中的任務,先運行排在后面的任務竞思。等到IO設備返回了結果表谊,再回過頭,把掛起的任務繼續(xù)執(zhí)行下去盖喷。

于是爆办,JavaScript任務可以分兩種,同步任務和異步任務

同步任務

同步任務指的是传蹈,在主線程上排隊執(zhí)行的任務押逼,只有前一個任務執(zhí)行完畢步藕,才能執(zhí)行后一個任務。

異步任務

異步任務指的是挑格,不進入主線程咙冗,而進入任務隊列中的任務,只有任務隊列通知主線程漂彤,某個異步任務可以執(zhí)行了雾消,這個任務才會進入主線程執(zhí)行。

異步執(zhí)行的具體運行機制

同步執(zhí)行也是如此挫望,同步執(zhí)行可以被視為沒有異步任務的異步執(zhí)行

  1. 所有同步任務都在主線程上執(zhí)行立润,形成一個執(zhí)行棧
  2. 主線程之外,還存在一個"任務隊列"媳板,只要異步任務有了運行結果桑腮,就在"任務隊列"之中放置一個事件。
  3. 一旦"執(zhí)行棧"中的所有同步任務執(zhí)行完畢蛉幸,系統(tǒng)就會讀取"任務隊列"破讨,看看里面有哪些事件。那些對應的異步任務奕纫,于是結束等待狀態(tài)提陶,進入執(zhí)行棧,開始執(zhí)行匹层。
  4. 主線程不斷重復上面的第三步隙笆。

只要主線程空了,就會去讀取"任務隊列"升筏,這就是JavaScript的運行機制撑柔。這個過程會不斷重復。

主線程和任務隊列的示意圖

3. 事件和回調(diào)函數(shù)

Event queue - -- 任務隊列 是一個事件的隊列您访,IO設備完成一項任務乏冀,就會在任務隊列中添加一個事件,表明相關的異步任務可以進行“執(zhí)行椦笾唬”了,主程序讀取任務隊列昼捍,就是讀取里面的事件识虚。

Event queue 中的事件,除了IO設備的事件妒茬,還包括一些用戶的操作事件担锤,如鼠標點擊,頁面滾動等等乍钻,只要指定過回調(diào)函數(shù)肛循,這些事件都會進入任務隊列铭腕,當主線程開始執(zhí)行異步任務,就是執(zhí)行對應的回調(diào)函數(shù)多糠。

"任務隊列"是一個先進先出的數(shù)據(jù)結構累舷,排在前面的事件,優(yōu)先被主線程讀取夹孔。主線程的讀取過程基本上是自動的被盈,只要執(zhí)行棧一清空,"任務隊列"上第一位的事件就自動進入主線程搭伤。

但是只怎,由于存在"定時器"功能,主線程首先要檢查一下執(zhí)行時間怜俐,某些事件只有到了規(guī)定的時間身堡,才能返回主線程。

4. Event Loop

主線程從"任務隊列"中讀取事件拍鲤,這個過程是循環(huán)不斷的贴谎,所以整個的這種運行機制又稱為Event Loop(事件循環(huán))。

image

主程序運行時殿漠,產(chǎn)生堆和棧赴精,棧中的代碼調(diào)用各位外部的WEBAPI,他們在任務隊列中加入各種事件绞幌,如圖中的Click蕾哟,Load,Done莲蜘。只要棧中的代碼執(zhí)行完畢谭确,主線程就會去讀取"任務隊列",依次執(zhí)行那些事件所對應的回調(diào)函數(shù)票渠。

棧中的代碼(同步任務)總是在任務隊列(異步任務)之前執(zhí)行

5. 定時器

除了放置異步任務的事件逐哈,"任務隊列"還可以放置定時事件

定時器功能主要由setTimeout()和setInterval()這兩個函數(shù)來完成,它們的內(nèi)部運行機制完全一樣问顷,區(qū)別在于前者指定的代碼是一次性執(zhí)行昂秃,后者則為反復執(zhí)行。以下主要討論setTimeout()杜窄。

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

上面代碼的執(zhí)行結果總是2肠骆,1,因為只有在執(zhí)行完第二行以后塞耕,系統(tǒng)才會去執(zhí)行"任務隊列"中的回調(diào)函數(shù)蚀腿,盡管設置的推遲毫秒數(shù)為0.

HTML5標準規(guī)定了setTimeout()的第二個參數(shù)的最小值(最短間隔),不得低于4毫秒扫外,如果低于這個值莉钙,就會自動增加廓脆。

需要注意的是,setTimeout()只是將事件插入了"任務隊列"磁玉,必須等到當前代碼(執(zhí)行棧)執(zhí)行完停忿,主線程才會去執(zhí)行它指定的回調(diào)函數(shù)。要是當前代碼耗時很長蜀涨,有可能要等很久瞎嬉,所以并沒有辦法保證,回調(diào)函數(shù)一定會在setTimeout()指定的時間執(zhí)行厚柳。

6. Node.js的Event Loop

Node.js也是單線程的Event Loop氧枣,但是它的運行機制不同于瀏覽器環(huán)境。

  • nodejs的event是基于libuv别垮,而瀏覽器的event loop則在html5的規(guī)范中明確定義便监。
  • libuv已經(jīng)對event loop作出了實現(xiàn),而html5規(guī)范中只是定義了瀏覽器中event loop的模型碳想,具體實現(xiàn)留給了瀏覽器廠商烧董。
image

根據(jù)上圖,Node.js的運行機制如下胧奔。

(1)V8引擎解析JavaScript腳本逊移。

(2)解析后的代碼,調(diào)用Node API龙填。

(3)libuv庫負責Node API的執(zhí)行胳泉。它將不同的任務分配給不同的線程,形成一個Event Loop(事件循環(huán))岩遗,以異步的方式將任務的執(zhí)行結果返回給V8引擎扇商。

(4)V8引擎再將結果返回給用戶。

除了setTimeout和setInterval這兩個方法宿礁,Node.js還提供了另外兩個與"任務隊列"有關的方法:process.nextTicksetImmediate案铺。它們可以幫助我們加深對"任務隊列"的理解。

  • process.nextTick方法可以在當前"執(zhí)行棧"的尾部----下一次Event Loop(主線程讀取"任務隊列")之前----觸發(fā)回調(diào)函數(shù),它指定的任務總是發(fā)生在所有異步任務之前梆靖。
  • setImmediate方法則是在當前"任務隊列"的尾部添加事件控汉,也就是說,它指定的任務總是在下一次Event Loop時執(zhí)行返吻,這與setTimeout(fn, 0)很像暇番。
process.nextTick(function A() {
  console.log(1);
  process.nextTick(function B(){console.log(2);});
});

setTimeout(function timeout() {
  console.log('Yo~~');
}, 0)
// 1
// 2
// Yo~~

上面代碼中,由于process.nextTick方法指定的回調(diào)函數(shù)思喊,總是在當前"執(zhí)行棧"的尾部觸發(fā),所以不僅函數(shù)A比setTimeout指定的回調(diào)函數(shù)timeout先執(zhí)行次酌,而且函數(shù)B也比timeout先執(zhí)行恨课。這說明舆乔,如果有多個process.nextTick語句(不管它們是否嵌套),將全部在當前"執(zhí)行棧"執(zhí)行剂公。

現(xiàn)在希俩,再看setImmediate

setImmediate(function (){
  setImmediate(function A() {
    console.log(1);
    setImmediate(function B(){console.log(2);});
  });

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

上面代碼中,setImmediate與setTimeout(fn,0)各自添加了一個回調(diào)函數(shù)A和timeout纲辽,都是在下一次Event Loop觸發(fā)颜武。那么,哪個回調(diào)函數(shù)先執(zhí)行呢拖吼?答案是不確定鳞上。運行結果可能是1--TIMEOUT FIRED--2,也可能是TIMEOUT FIRED--1--2吊档。

令人困惑的是篙议,Node.js文檔中稱,setImmediate指定的回調(diào)函數(shù)怠硼,總是排在setTimeout前面鬼贱。實際上,這種情況只發(fā)生在遞歸調(diào)用的時候香璃。

setImmediate(function (){
  setImmediate(function A() {
    console.log(1);
    setImmediate(function B(){console.log(2);});
  });

  setTimeout(function timeout() {
    console.log('TIMEOUT FIRED');
  }, 0);
});
// 1
// TIMEOUT FIRED
// 2

上面代碼中这难,setImmediate和setTimeout被封裝在一個setImmediate里面,它的運行結果總是1--TIMEOUT FIRED--2葡秒,這時函數(shù)A一定在timeout前面觸發(fā)姻乓。至于2排在TIMEOUT FIRED的后面(即函數(shù)B在timeout后面觸發(fā)),是因為setImmediate總是將事件注冊到下一輪Event Loop同云,所以函數(shù)A和timeout是在同一輪Loop執(zhí)行糖权,而函數(shù)B在下一輪Loop執(zhí)行。

process.nextTick和setImmediate的一個重要區(qū)別:多個process.nextTick語句總是在當前"執(zhí)行棧"一次執(zhí)行完炸站,多個setImmediate可能則需要多次loop才能執(zhí)行完星澳。

js 和 node 的原理知識還是很多地方都不甚了解,加油努力學習旱易!

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末禁偎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子阀坏,更是在濱河造成了極大的恐慌如暖,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件忌堂,死亡現(xiàn)場離奇詭異盒至,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門枷遂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來樱衷,“玉大人,你說我怎么就攤上這事酒唉【毓穑” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵痪伦,是天一觀的道長侄榴。 經(jīng)常有香客問我,道長网沾,這世上最難降的妖魔是什么癞蚕? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮绅这,結果婚禮上涣达,老公的妹妹穿的比我還像新娘。我一直安慰自己证薇,他們只是感情好度苔,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著浑度,像睡著了一般寇窑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上箩张,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天甩骏,我揣著相機與錄音,去河邊找鬼先慷。 笑死饮笛,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的论熙。 我是一名探鬼主播福青,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼脓诡!你這毒婦竟也來了无午?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤祝谚,失蹤者是張志新(化名)和其女友劉穎宪迟,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體交惯,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡次泽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年穿仪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片箕憾。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡牡借,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出袭异,到底是詐尸還是另有隱情,我是刑警寧澤炬藤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布御铃,位于F島的核電站,受9級特大地震影響沈矿,放射性物質(zhì)發(fā)生泄漏上真。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一羹膳、第九天 我趴在偏房一處隱蔽的房頂上張望睡互。 院中可真熱鬧,春花似錦陵像、人聲如沸就珠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妻怎。三九已至,卻和暖如春泞歉,著一層夾襖步出監(jiān)牢的瞬間逼侦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工腰耙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留榛丢,地道東北人。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓挺庞,卻偏偏與公主長得像晰赞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子挠阁,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359

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