js的單線程休雌,異步及回調(diào)函數(shù)

最近本人對于js的運(yùn)行機(jī)制灶壶,特別是異步,還有回調(diào)函數(shù)感覺很亂杈曲,于是參考了很多有用的博客(博客原文地址會在文末給出)驰凛,整理如下:


js單線程

我們都知道,Javascript語言的執(zhí)行環(huán)境是"單線程"(single thread)担扑。也就是說恰响,瀏覽器只分配給js一個(gè)主線程用來執(zhí)行任務(wù)即函數(shù),但是每次只能執(zhí)行一個(gè)任務(wù)涌献,只有等到當(dāng)前任務(wù)執(zhí)行完成后胚宦,才執(zhí)行后面的任務(wù),這些任務(wù)形成一個(gè)任務(wù)隊(duì)列排隊(duì)等候執(zhí)行燕垃,這一點(diǎn)和我們?nèi)粘5呐抨?duì)很像枢劝,譬如排隊(duì)買奶茶,只有等到前面一個(gè)人買完奶茶付完錢卜壕,排在他后面的人才可以買奶茶呈野。但是,當(dāng)前面一個(gè)任務(wù)很耗時(shí)時(shí)印叁,后面的任務(wù)就不得不等著被冒,這時(shí)候整個(gè)程序的執(zhí)行效率就會下降,就像我們平時(shí)遇到的瀏覽器無響應(yīng)即頁面假死往往是因?yàn)槟扯蝚s代碼長時(shí)間運(yùn)行如死循環(huán)轮蜕,導(dǎo)致頁面卡死昨悼,后面的任務(wù)無法執(zhí)行。

講到j(luò)s的單線程跃洛,就不得不來了解一下瀏覽器

瀏覽器多線程

瀏覽器主要線程

如圖率触,瀏覽器是一個(gè)多線程的執(zhí)行環(huán)境,在瀏覽器的內(nèi)核中分配了多個(gè)線程汇竭,其中瀏覽器常駐三大線程: js引擎線程葱蝗,GUI渲染線程穴张,瀏覽器事件觸發(fā)線程。最主要的線程之一即是js引擎的線程两曼。由于這三個(gè)線程同時(shí)要訪問DOM樹皂甘,所以為了線程安全,瀏覽器內(nèi)部需要做互斥即當(dāng)JS引擎在執(zhí)行代碼的時(shí)候悼凑,界面渲染和事件響應(yīng)兩個(gè)線程是被暫停的偿枕。而所以當(dāng)JS出現(xiàn)死循環(huán),瀏覽器無法響應(yīng)點(diǎn)擊户辫,也無法更新界面渐夸。

前面說到,前端會有一些任務(wù)十分耗時(shí)渔欢,而由于js是單線程使用會降低執(zhí)行效率墓塌,這些耗時(shí)的任務(wù)如網(wǎng)絡(luò)請求,定時(shí)器和事件監(jiān)聽奥额。所以桃纯,瀏覽器為這些耗時(shí)任務(wù)開辟了另外的線程,主要包括http請求線程披坏,瀏覽器定時(shí)觸發(fā)器态坦,瀏覽器事件觸發(fā)線程,這些任務(wù)是異步的(見圖)棒拂。(詳細(xì)過程見此博文伞梯,博主講得很好~ 個(gè)人建議必須看一看哦~)

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

說回剛剛的js單線程,為了不讓前面的耗時(shí)任務(wù)導(dǎo)致的問題出現(xiàn)帚屉,js的設(shè)計(jì)者把js的任務(wù)分為同步任務(wù)和異步任務(wù)谜诫。同步任務(wù)指的是,在主線程上排隊(duì)執(zhí)行的任務(wù)攻旦,只有前一個(gè)任務(wù)執(zhí)行完畢喻旷,才能執(zhí)行后一個(gè)任務(wù);異步任務(wù)指的是牢屋,不進(jìn)入主線程且预、而進(jìn)入"任務(wù)隊(duì)列"(task queue)的任務(wù),只有"任務(wù)隊(duì)列"通知主線程烙无,某個(gè)異步任務(wù)可以執(zhí)行了锋谐,該任務(wù)才會進(jìn)入主線程執(zhí)行。如我們剛剛所講到的瀏覽器為網(wǎng)絡(luò)請求開辟的http請求線程就是異步任務(wù)截酷。

具體來說涮拗,異步執(zhí)行的運(yùn)行機(jī)制如下。(同步執(zhí)行也是如此,因?yàn)樗梢员灰暈闆]有異步任務(wù)的異步執(zhí)行三热。)

(1)所有同步任務(wù)都在主線程上執(zhí)行鼓择,形成一個(gè)[執(zhí)行棧]。
(2)主線程之外就漾,還存在一個(gè)"任務(wù)隊(duì)列"(task queue)呐能。只要異步任務(wù)有了運(yùn)行結(jié)果,就在"任務(wù)隊(duì)列"之中放置一個(gè)事件从藤。
(3)一旦"執(zhí)行棧"中的所有同步任務(wù)執(zhí)行完畢催跪,系統(tǒng)就會讀取"任務(wù)隊(duì)列"锁蠕,看看里面有哪些事件夷野。那些對應(yīng)的異步任務(wù),于是結(jié)束等待狀態(tài)荣倾,進(jìn)入執(zhí)行棧悯搔,開始執(zhí)行。
(4)主線程不斷重復(fù)上面的第三步舌仍。

(參考與阮一峰老師的博文JavaScript 運(yùn)行機(jī)制詳解:再談Event Loop

回調(diào)函數(shù)

有了以上了解我們可以知道妒貌,主線程內(nèi)的同步任務(wù)執(zhí)行完畢后,就會執(zhí)行排在任務(wù)隊(duì)列第一位的異步任務(wù)铸豁,這個(gè)過程不斷重復(fù)灌曙。

當(dāng)主線程開始執(zhí)行異步任務(wù),實(shí)際就是執(zhí)行對應(yīng)的回調(diào)函數(shù)节芥。

我們來看一下例子:

setTimeout(function(){
    console.log('Hello');
}在刺,10);

執(zhí)行這段代碼,瀏覽器異步執(zhí)行計(jì)時(shí)操作(注意這里的瀏覽器模型定時(shí)計(jì)數(shù)器并不是由JavaScript引擎計(jì)數(shù)的)头镊,當(dāng)10ms到了之后蚣驼,就會觸發(fā)定時(shí)事件,這時(shí)就會把其中的回調(diào)函數(shù)放到任務(wù)隊(duì)列中相艇,所以當(dāng)主線程空閑時(shí)在任務(wù)隊(duì)列中“讀取”并且執(zhí)行的就是回調(diào)函數(shù)颖杏。

異步任務(wù)必須指定回調(diào)函數(shù)。

Event Loop

主線程從"任務(wù)隊(duì)列"中讀取事件坛芽,這個(gè)過程是循環(huán)不斷的留储,所以整個(gè)的這種運(yùn)行機(jī)制又稱為Event Loop(事件循環(huán))。

圖片來自Philip Roberts的演講《Help, I'm stuck in an event-loop》

如圖咙轩,WebAPIs就是js線程外部的api如我們剛剛所說瀏覽器為異步任務(wù)所開辟的線程欲鹏。而任務(wù)隊(duì)列就是callbackqueue,我們知道任務(wù)隊(duì)列中其實(shí)就是各種異步任務(wù)的回調(diào)函數(shù)臭墨,從callbackqueue的直譯“回調(diào)隊(duì)列”也可看出赔嚎。而heap堆和stack棧組成了js的主線程,當(dāng)stack中的函數(shù)執(zhí)行完成后,就會在callbackqueue中尋找下一個(gè)任務(wù)并把它推入棧尤误,這個(gè)尋找的過程就叫event loop(事件循環(huán))侠畔。


看了上面你是不是對js的運(yùn)行機(jī)制有了了解呢~
我們把學(xué)會的知識來用一用:

js中的異步之定時(shí)器

說起js的異步,很多人第一反應(yīng)是Ajax损晤,但其實(shí)js中最基礎(chǔ)的異步就是setTimeout/setInterval软棺。(小伙伴可不要把定時(shí)器忘了哦:))

我們以setTimeout為例,setTimeout接受兩個(gè)參數(shù)尤勋,第一個(gè)是回調(diào)函數(shù)喘落,第二個(gè)是推遲執(zhí)行的毫秒數(shù)。

我們看看例子:

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

是不是以為打印的順序是0,1最冰?但大家注意哦瘦棋,這時(shí)候?yàn)g覽器打印的順序是1,0暖哨。大家可能疑問了赌朋,setTimeout中設(shè)置的推遲執(zhí)行的毫秒數(shù)是0呀,不就是立即執(zhí)行的意思嗎篇裁。大家還記得剛剛我們說了當(dāng)有耗時(shí)任務(wù)時(shí)沛慢,會把它放在任務(wù)隊(duì)列中等待主線程空閑然后再執(zhí)行,實(shí)際在執(zhí)行程序的時(shí)候达布,瀏覽器會默認(rèn)setTimeout以及ajax請求這一類的方法都是耗時(shí)程序(盡管可能不耗時(shí))团甲,也就是上面說過的瀏覽器會為其異步開辟線程。所以此時(shí)的setTimeout盡管它推遲時(shí)間為0黍聂,但是js不會立即執(zhí)行躺苦,而是把它加入任務(wù)隊(duì)列,當(dāng)執(zhí)行完執(zhí)行棧的同步任務(wù)也就是打印1后分冈,再執(zhí)行setTimeout的回調(diào)函數(shù)圾另,打印0。

總之雕沉,setTimeout(fn,0)的含義是集乔,指定某個(gè)任務(wù)在主線程最早可得的空閑時(shí)間執(zhí)行,也就是說坡椒,盡可能早得執(zhí)行扰路。它在"任務(wù)隊(duì)列"的尾部添加一個(gè)事件,因此要等到同步任務(wù)和"任務(wù)隊(duì)列"現(xiàn)有的事件都處理完倔叼,才會得到執(zhí)行汗唱。

所以注意的是,setTimeout()只是將事件插入了任務(wù)隊(duì)列丈攒,必須等到當(dāng)前代碼(執(zhí)行棧)執(zhí)行完哩罪,主線程才會去執(zhí)行它指定的回調(diào)函數(shù)授霸。但如果當(dāng)前任務(wù)十分耗時(shí),需要等很久际插,所以并沒有辦法保證碘耳,回調(diào)函數(shù)一定會在setTimeout()指定的時(shí)間執(zhí)行,比如說你指定10ms后執(zhí)行框弛,但是當(dāng)前的任務(wù)執(zhí)行了20ms辛辨,所以setTimeout的回調(diào)函數(shù)并不能在10ms后立即執(zhí)行,可能要20ms后瑟枫,如果setTimeout在任務(wù)隊(duì)列中不是排第一位斗搞,可能還不止20ms。

js異步編程的方法

這個(gè)我還不是很懂~大家可以參考阮一峰老師的Javascript異步編程的4種方法

希望一包的文章可以幫到你們~


參考文章:

http://blog.csdn.net/qq_22855325/article/details/72958345(贊~)
https://www.cnblogs.com/woodyblog/p/6061671.html(重點(diǎn)參考這篇文章博主寫得很好~)
http://blog.csdn.net/kfanning/article/details/5768776(贊~)
http://www.ruanyifeng.com/blog/2014/10/event-loop.html(阮一峰老師嘛~)
http://www.cnblogs.com/smght/p/4368399.html#3575993(贊~)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末慷妙,一起剝皮案震驚了整個(gè)濱河市僻焚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌景殷,老刑警劉巖溅呢,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件澡屡,死亡現(xiàn)場離奇詭異猿挚,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)驶鹉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門绩蜻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人室埋,你說我怎么就攤上這事办绝。” “怎么了姚淆?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵孕蝉,是天一觀的道長。 經(jīng)常有香客問我腌逢,道長降淮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任搏讶,我火速辦了婚禮佳鳖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘媒惕。我一直安慰自己系吩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布妒蔚。 她就那樣靜靜地躺著穿挨,像睡著了一般月弛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上科盛,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天尊搬,我揣著相機(jī)與錄音,去河邊找鬼土涝。 笑死佛寿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的但壮。 我是一名探鬼主播冀泻,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蜡饵!你這毒婦竟也來了弹渔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤溯祸,失蹤者是張志新(化名)和其女友劉穎肢专,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體焦辅,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡博杖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了筷登。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剃根。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖前方,靈堂內(nèi)的尸體忽然破棺而出狈醉,到底是詐尸還是另有隱情,我是刑警寧澤惠险,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布苗傅,位于F島的核電站,受9級特大地震影響班巩,放射性物質(zhì)發(fā)生泄漏渣慕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一趣竣、第九天 我趴在偏房一處隱蔽的房頂上張望摇庙。 院中可真熱鬧,春花似錦遥缕、人聲如沸卫袒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽夕凝。三九已至宝穗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間码秉,已是汗流浹背逮矛。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留转砖,地道東北人须鼎。 一個(gè)月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像府蔗,于是被迫代替她去往敵國和親晋控。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

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

  • ———-正文開始———- 最近發(fā)現(xiàn)有不少介紹JS單線程運(yùn)行機(jī)制的文章,但是發(fā)現(xiàn)很多都僅僅是介紹某一部分的知識不铆,而且...
    流動碼文閱讀 2,207評論 7 32
  • 弄懂js異步 講異步之前蝌焚,我們必須掌握一個(gè)基礎(chǔ)知識-event-loop。 我們知道JavaScript的一大特點(diǎn)...
    DCbryant閱讀 2,714評論 0 5
  • 1.背景介紹 什么是同步誓斥,什么是異步? 同步指的是一次只能完成一件任務(wù)只洒。如果有多個(gè)任務(wù),就必須排隊(duì)岖食,前面一個(gè)任務(wù)完...
    風(fēng)流司郎中閱讀 730評論 1 6
  • 我離開家已有十六年了红碑,十六年來只回過四次家舞吭,也都是行色匆匆泡垃,沒有在老屋睡上一次覺,更沒有陪著娘坐上一天羡鸥。 我少年睡...
    永無休止閱讀 1,161評論 13 18
  • 凌晨兩點(diǎn)蔑穴,聽靈魂哭泣 染了血色的雨水,斑駁了俊顏 墨色絲線惧浴,斷了一根一根 被扼住的喉嚨存和,發(fā)出沙啞嘶鳴 親手剜下心臟...
    山半閱讀 436評論 37 24