EventLoop

為什么JavaScript是單線程?

JavaScript語言的一大特點就是單線程毫胜,也就是說狂打,同一個時間只能做一件事擂煞。那么,為什么JavaScript不能有多個線程呢趴乡?這樣能提高效率啊对省。

JavaScript的單線程,與它的用途有關(guān)晾捏。作為瀏覽器腳本語言蒿涎,JavaScript的主要用途是與用戶互動,以及操作DOM惦辛。這決定了它只能是單線程劳秋,否則會帶來很復(fù)雜的同步問題。比如,假定JavaScript同時有兩個線程俗批,一個線程在某個DOM節(jié)點上添加內(nèi)容,另一個線程刪除了這個節(jié)點市怎,這時瀏覽器應(yīng)該以哪個線程為準(zhǔn)岁忘?

所以,為了避免復(fù)雜性区匠,從一誕生干像,JavaScript就是單線程,這已經(jīng)成了這門語言的核心特征驰弄,將來也不會改變麻汰。

為了利用多核CPU的計算能力,HTML5提出Web Worker標(biāo)準(zhǔn)戚篙,允許JavaScript腳本創(chuàng)建多個線程五鲫,但是子線程完全受主線程控制,且不得操作DOM岔擂。所以位喂,這個新標(biāo)準(zhǔn)并沒有改變JavaScript單線程的本質(zhì)。

任務(wù)隊列

單線程就意味著乱灵,所有任務(wù)需要排隊塑崖,前一個任務(wù)結(jié)束,才會執(zhí)行后一個任務(wù)痛倚。如果前一個任務(wù)耗時很長规婆,后一個任務(wù)就不得不一直等著。

所有任務(wù)可以分成兩種蝉稳,一種是同步任務(wù)(synchronous)抒蚜,另一種是異步任務(wù)(asynchronous)。同步任務(wù)指的是颠区,在主線程上排隊執(zhí)行的任務(wù)削锰,只有前一個任務(wù)執(zhí)行完畢,才能執(zhí)行后一個任務(wù)毕莱;異步任務(wù)指的是器贩,不進入主線程、而進入"任務(wù)隊列"(task queue)的任務(wù)朋截,只有"任務(wù)隊列"通知主線程蛹稍,某個異步任務(wù)可以執(zhí)行了,該任務(wù)才會進入主線程執(zhí)行部服。

具體來說唆姐,異步執(zhí)行的運行機制如下。(同步執(zhí)行也是如此廓八,因為它可以被視為沒有異步任務(wù)的異步執(zhí)行奉芦。)

(1)所有同步任務(wù)都在主線程上執(zhí)行赵抢,形成一個執(zhí)行棧(execution context stack)。

(2)主線程之外声功,還存在一個"任務(wù)隊列"(task queue)烦却。只要異步任務(wù)有了運行結(jié)果,就在"任務(wù)隊列"之中放置一個事件先巴。

(3)一旦"執(zhí)行棧"中的所有同步任務(wù)執(zhí)行完畢其爵,系統(tǒng)就會讀取"任務(wù)隊列",看看里面有哪些事件伸蚯。那些對應(yīng)的異步任務(wù)摩渺,于是結(jié)束等待狀態(tài),進入執(zhí)行棧剂邮,開始執(zhí)行摇幻。

(4)主線程不斷重復(fù)上面的第三步。

主線程和任務(wù)隊列的示意圖

只要主線程空了挥萌,就會去讀取"任務(wù)隊列"囚企,這就是JavaScript的運行機制。這個過程會不斷重復(fù)瑞眼。

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

"任務(wù)隊列"是一個事件的隊列(也可以理解成消息的隊列)龙宏,IO設(shè)備完成一項任務(wù),就在"任務(wù)隊列"中添加一個事件伤疙,表示相關(guān)的異步任務(wù)可以進入"執(zhí)行棧"了银酗。主線程讀取"任務(wù)隊列",就是讀取里面有哪些事件徒像。

"任務(wù)隊列"中的事件黍特,除了IO設(shè)備的事件以外,還包括一些用戶產(chǎn)生的事件(比如鼠標(biāo)點擊锯蛀、頁面滾動等等)灭衷。只要指定過回調(diào)函數(shù),這些事件發(fā)生時就會進入"任務(wù)隊列"旁涤,等待主線程讀取翔曲。

所謂"回調(diào)函數(shù)"(callback),就是那些會被主線程掛起來的代碼劈愚。異步任務(wù)必須指定回調(diào)函數(shù)瞳遍,當(dāng)主線程開始執(zhí)行異步任務(wù),就是執(zhí)行對應(yīng)的回調(diào)函數(shù)菌羽。

"任務(wù)隊列"是一個先進先出的數(shù)據(jù)結(jié)構(gòu)掠械,排在前面的事件,優(yōu)先被主線程讀取。主線程的讀取過程基本上是自動的猾蒂,只要執(zhí)行棧一清空均唉,"任務(wù)隊列"上第一位的事件就自動進入主線程。但是肚菠,由于存在后文提到的"定時器"功能浸卦,主線程首先要檢查一下執(zhí)行時間,某些事件只有到了規(guī)定的時間案糙,才能返回主線程。

Event Loop

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

Event Loop

主線程運行的時候炉抒,產(chǎn)生堆(heap)和棧(stack)奢讨,棧中的代碼調(diào)用各種外部API,它們在"任務(wù)隊列"中加入各種事件(click焰薄,load拿诸,done)。只要棧中的代碼執(zhí)行完畢塞茅,主線程就會去讀取"任務(wù)隊列"亩码,依次執(zhí)行那些事件所對應(yīng)的回調(diào)函數(shù)。

執(zhí)行棧中的代碼(同步任務(wù))野瘦,總是在讀取"任務(wù)隊列"(異步任務(wù))之前執(zhí)行描沟。

var req = new XMLHttpRequest();
    req.open('GET', url);    
    req.onload = function (){};    
    req.onerror = function (){};    
    req.send();

上面代碼中的req.send方法是Ajax操作向服務(wù)器發(fā)送數(shù)據(jù),它是一個異步任務(wù)鞭光,意味著只有當(dāng)前腳本的所有代碼執(zhí)行完吏廉,系統(tǒng)才會去讀取"任務(wù)隊列"。所以惰许,它與下面的寫法等價

  var req = new XMLHttpRequest();
    req.open('GET', url);
    req.send();
    req.onload = function (){};    
    req.onerror = function (){};   

也就是說席覆,指定回調(diào)函數(shù)的部分(onload和onerror),在send()方法的前面或后面無關(guān)緊要汹买,因為它們屬于執(zhí)行棧的一部分佩伤,系統(tǒng)總是執(zhí)行完它們,才會去讀取"任務(wù)隊列"晦毙。

宏任務(wù)和微任務(wù)

頁面渲染事件畦戒,各種IO的完成事件等隨時被添加到任務(wù)隊列中,一直會保持先進先出的原則執(zhí)行结序,我們不能準(zhǔn)確地控制這些事件被添加到任務(wù)隊列中的位置障斋。但是這個時候突然有高優(yōu)先級的任務(wù)需要盡快執(zhí)行,那么一種類型的任務(wù)就不合適了,所以引入了微任務(wù)隊列垃环。

不同的異步任務(wù)被分為:宏任務(wù)和微任務(wù)

宏任務(wù):

  • script(整體代碼)
  • setTimeout()
  • setInterval()
  • postMessage
  • I/O
  • UI交互事件

微任務(wù):

  • Promise.then
  • Object.observe
  • MutationObserver
  • process.nextTick(Node.js 環(huán)境)

宏任務(wù)和微任務(wù)的執(zhí)行順序:

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末邀层,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子遂庄,更是在濱河造成了極大的恐慌寥院,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涛目,死亡現(xiàn)場離奇詭異秸谢,居然都是意外死亡,警方通過查閱死者的電腦和手機霹肝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門估蹄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人沫换,你說我怎么就攤上這事臭蚁。” “怎么了讯赏?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵垮兑,是天一觀的道長。 經(jīng)常有香客問我漱挎,道長系枪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任磕谅,我火速辦了婚禮嗤无,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘怜庸。我一直安慰自己当犯,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布割疾。 她就那樣靜靜地躺著嚎卫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宏榕。 梳的紋絲不亂的頭發(fā)上拓诸,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音麻昼,去河邊找鬼奠支。 笑死,一個胖子當(dāng)著我的面吹牛抚芦,可吹牛的內(nèi)容都是我干的倍谜。 我是一名探鬼主播迈螟,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼尔崔!你這毒婦竟也來了答毫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤季春,失蹤者是張志新(化名)和其女友劉穎洗搂,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體载弄,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡耘拇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了宇攻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惫叛。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖尺碰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情译隘,我是刑警寧澤亲桥,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站固耘,受9級特大地震影響题篷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜厅目,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一番枚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧损敷,春花似錦葫笼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至诱桂,卻和暖如春洋丐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挥等。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工友绝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肝劲。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓迁客,卻偏偏與公主長得像郭宝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子哲泊,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

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