單線程JavaScript


什么是線程

由于JavaScript是單線程語言,因此速勇,在一個(gè)進(jìn)程上,只能運(yùn)行一個(gè)線程坎拐,而不能多個(gè)線程同時(shí)運(yùn)行烦磁。也就是說JavaScript不允許多個(gè)線程共享內(nèi)存空間养匈。因此,如果有多個(gè)線程想同時(shí)運(yùn)行都伪,則需采取排隊(duì)的方式呕乎,即只有當(dāng)前一個(gè)線程執(zhí)行完畢,后一個(gè)線程才開始執(zhí)行陨晶。

Heap 猬仁、Stack、 Queue

堆:對(duì)象被分配在一個(gè)堆中先誉,用以表示一個(gè)內(nèi)存中未被組織的區(qū)域湿刽。我們知道,在函數(shù)被調(diào)用之前褐耳,JavaScript引擎會(huì)對(duì)函數(shù)進(jìn)行編譯(詞法分析诈闺、語法分析、代碼生成)的工作铃芦。當(dāng)完成編譯時(shí)會(huì)將函數(shù)(這里不限于函數(shù)雅镊,JavaScript所有皆對(duì)象,除了undefined,null)放入堆中刃滓,分配內(nèi)存空間仁烹,等待執(zhí)行或者調(diào)用。
棧: 當(dāng)函數(shù)調(diào)用時(shí)咧虎,會(huì)形成一個(gè)"執(zhí)行棧"卓缰。我們看一個(gè)簡單的例子。

function bar(b){
  return b*2;
}
function foo(a){
  return bar(a * 3);
}
console.log(foo(1));  //6

當(dāng)JavaScript引擎在編譯階段老客,會(huì)將foo僚饭、bar置于堆中,分配內(nèi)存空間胧砰。當(dāng)調(diào)用foo()時(shí)鳍鸵,引擎創(chuàng)建了一個(gè)執(zhí)行棧,包含了foo函數(shù)的參數(shù)和局部變量尉间。當(dāng)在foo的詞法作用域中調(diào)用bar時(shí)偿乖,會(huì)將bar函數(shù)推入執(zhí)行棧,并置于foo函數(shù)之上哲嘲,同時(shí)包含bar函數(shù)的參數(shù)和局部變量贪薪。當(dāng)bar返回時(shí)(此例中bar函數(shù)調(diào)用并返回結(jié)果是瞬間完成的),bar函數(shù)出棧眠副。當(dāng)foo函數(shù)返回結(jié)果時(shí)画切,整個(gè)執(zhí)行棧就空了。此時(shí)囱怕,如果任務(wù)隊(duì)列中存在異步任務(wù)霍弹,則主線程會(huì)讀取任務(wù)隊(duì)列中的任務(wù)毫别。待會(huì)介紹任務(wù)隊(duì)列。

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

單線程就意味著典格,所有任務(wù)(線程)需要排隊(duì)岛宦,前一個(gè)任務(wù)結(jié)束,才會(huì)執(zhí)行后一個(gè)任務(wù)耍缴。如果前一個(gè)任務(wù)耗時(shí)很長砾肺,后一個(gè)任務(wù)不得不一直等待。

因此防嗡,所有任務(wù)可以分為兩種变汪,一種是同步任務(wù),一種是異步任務(wù)本鸣。同步任務(wù)指的是疫衩,在主線程上排隊(duì)執(zhí)行的任務(wù),只有前一個(gè)任務(wù)執(zhí)行完畢荣德,后一個(gè)任務(wù)才會(huì)執(zhí)行闷煤;異步任務(wù)指的是不進(jìn)入主線程、而進(jìn)入任務(wù)隊(duì)列的任務(wù)涮瞻,只有當(dāng)主線程上的所有同步任務(wù)執(zhí)行完畢之后鲤拿,主線程才會(huì)讀取任務(wù)隊(duì)列,開始執(zhí)行異步任務(wù)署咽。

任務(wù)隊(duì)列是一個(gè)事件的隊(duì)列(也可以理解成消息的隊(duì)列)近顷,IO設(shè)備完成一項(xiàng)任務(wù),就在"任務(wù)隊(duì)列"中添加一個(gè)事件宁否,表示相關(guān)的異步任務(wù)可以進(jìn)入"執(zhí)行棧"了窒升。主線程讀取"任務(wù)隊(duì)列",就是讀取里面有哪些事件慕匠。

“任務(wù)隊(duì)列”中的事件饱须,除了IO設(shè)備(ajax獲取服務(wù)器數(shù)據(jù))的事件以外,還包括一些用戶產(chǎn)生的事件(mousehover台谊、click蓉媳、scroll、keyup等)和定時(shí)器等锅铅。只要在事件中指定了回調(diào)函數(shù)酪呻,這些事件發(fā)生時(shí)就會(huì)進(jìn)入“任務(wù)隊(duì)列”,等待主線程讀取盐须。而主線程讀取任務(wù)隊(duì)列中的異步任務(wù)玩荠,主要就是讀取回調(diào)函數(shù)。

當(dāng)主線程的所有同步任務(wù)執(zhí)行(排隊(duì)執(zhí)行)完畢之后,就會(huì)讀取任務(wù)隊(duì)列中的異步任務(wù)阶冈,將異步任務(wù)推入執(zhí)行棧中執(zhí)行屉凯。任務(wù)隊(duì)列是一個(gè)先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),即排在前面的事件眼溶,優(yōu)先被主線程讀取。如果存在定時(shí)器晓勇,時(shí)間越短的越先進(jìn)入執(zhí)行棧堂飞。

因此,可以做一個(gè)簡單總結(jié):

  • 當(dāng)主線程開始執(zhí)行同步任務(wù)時(shí)绑咱,會(huì)創(chuàng)建一個(gè)"執(zhí)行棧"绰筛,每一個(gè)同步任務(wù)排隊(duì)執(zhí)行,只有前一個(gè)任務(wù)執(zhí)行完畢描融,才會(huì)執(zhí)行下一個(gè)任務(wù)。同時(shí),執(zhí)行棧與汗水的調(diào)用位置有關(guān)棚愤。
  • 當(dāng)主線程上的所有同步任務(wù)執(zhí)行完畢之后髓涯,主線程會(huì)讀取任務(wù)隊(duì)列上的異步任務(wù),并將異步任務(wù)推入執(zhí)行棧中開始執(zhí)行年叮。
  • 主線程不斷重復(fù)以上2個(gè)步驟具被。

setTimeout

明白了主線程執(zhí)行相關(guān)任務(wù)的思路后,來看看定時(shí)器只损。上面介紹到一姿,定時(shí)器是屬于任務(wù)隊(duì)列中的異步任務(wù)。因此會(huì)等待“執(zhí)行椩颈梗”上的所有同步任務(wù)執(zhí)行完畢之后叮叹,主線程計(jì)算定時(shí)器的執(zhí)行時(shí)間,再將事件推入“執(zhí)行棻妫”蛉顽。看一個(gè)簡單的例子终蒂。

function foo() {
    setTimeout(function() {
        console.log(1);
    }, 0)
    console.log(2);
}
function bar() {
    setTimeout(function() {
        console.log(3);
    }, 0);
    console.log(4);
}
foo();
bar();

這段函數(shù)的輸出結(jié)果為2, 4, 1, 3蜂林。做一個(gè)簡單的分析。

foo拇泣、bar函數(shù)的內(nèi)部有相同的結(jié)構(gòu)噪叙,都有一個(gè)定時(shí)器和console.log()函數(shù)。當(dāng)foo霉翔、bar函數(shù)調(diào)用時(shí)睁蕾,會(huì)形成一個(gè)“執(zhí)行棧”,主線程會(huì)先執(zhí)行“執(zhí)行椬涌簦”中的同步任務(wù)瀑凝,即console.log(2), console.log(4),而兩個(gè)定時(shí)器會(huì)被推入任務(wù)隊(duì)列中臭杰,等待執(zhí)行粤咪。當(dāng)主線程上的同步任務(wù)執(zhí)行完畢之后,結(jié)束定時(shí)器的等待渴杆,將任務(wù)隊(duì)列中的兩個(gè)異步任務(wù)推入“執(zhí)行椓戎Γ”中執(zhí)行,因此輸出的順序?yàn)?, 4, 1, 3磁奖。

定時(shí)器的第一個(gè)參數(shù)是一個(gè)函數(shù)囊拜,第二個(gè)參數(shù)是推遲執(zhí)行的毫秒數(shù)。從函數(shù)的定義上看比搭,如果將時(shí)間設(shè)定為0冠跷,此時(shí)應(yīng)該是立即執(zhí)行定時(shí)器才對(duì),為什么輸出順序會(huì)不同呢身诺?

需要注意的是蜜托,setTimeout()只是將回調(diào)函數(shù)插入到“任務(wù)隊(duì)列”中,因此必須等到主線程上的同步任務(wù)全部執(zhí)行完畢霉赡,主線程才會(huì)執(zhí)行任務(wù)隊(duì)列中的異步任務(wù)盗冷,并且,setTimeout會(huì)等到同步任務(wù)執(zhí)行完畢之后同廉,再等到任務(wù)隊(duì)列中的異步任務(wù)執(zhí)行完畢之后才開始執(zhí)行仪糖。setTimeout的第二個(gè)參數(shù)只能確保任務(wù)在指定的時(shí)間之后執(zhí)行,而不能保證一定就在該時(shí)間之后立即執(zhí)行迫肖,是否能夠立即執(zhí)行锅劝,取決于“執(zhí)行棧”中的任務(wù)數(shù)量蟆湖。

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

function bar() {
    setTimeout(function() {
        console.log(3);
    }, 1000);
    console.log(4);
}

function baz() {
    setTimeout(function() {
        console.log(5);
    }, 0)
    console.log(6);
}
foo();
bar();
baz();
//結(jié)果: 2, 4, 6, 5, 3, 1;

主線程上的同步任務(wù)按照?qǐng)?zhí)行棧排隊(duì)執(zhí)行故爵,任務(wù)隊(duì)列上的定時(shí)器按照時(shí)間長短排隊(duì)執(zhí)行。時(shí)間越短隅津,越早進(jìn)入“執(zhí)行椢艽梗”,越早被主線程執(zhí)行伦仍。也就是說结窘,先進(jìn)入任務(wù)隊(duì)列的任務(wù)先執(zhí)行。

如果換一種函數(shù)的調(diào)用位置
baz();
foo();
bar();
//此時(shí)的結(jié)果: 6, 2, 4, 5, 3, 1

從上面的兩種運(yùn)行結(jié)果可以看出充蓝,

同步任務(wù)取決于函數(shù)的調(diào)用位置隧枫,不同的調(diào)用位置喉磁,進(jìn)入執(zhí)行棧的位置就不同,主線程執(zhí)行的順序就不同

異步任務(wù)的執(zhí)行與函數(shù)的調(diào)用位置無關(guān)官脓,只取決于執(zhí)行棧的任務(wù)數(shù)量协怒,當(dāng)同步任務(wù)執(zhí)行完畢之后,才會(huì)開始執(zhí)行異步任務(wù)卑笨,并且遵循先進(jìn)入任務(wù)隊(duì)列的事件先執(zhí)行的原則孕暇。
參考文獻(xiàn):http://www.cnblogs.com/Uncle-Keith/p/6436047.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市赤兴,隨后出現(xiàn)的幾起案子芭商,更是在濱河造成了極大的恐慌,老刑警劉巖搀缠,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異近迁,居然都是意外死亡艺普,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門鉴竭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來歧譬,“玉大人,你說我怎么就攤上這事搏存」宀剑” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵璧眠,是天一觀的道長缩焦。 經(jīng)常有香客問我,道長责静,這世上最難降的妖魔是什么袁滥? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮灾螃,結(jié)果婚禮上题翻,老公的妹妹穿的比我還像新娘。我一直安慰自己腰鬼,他們只是感情好嵌赠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著熄赡,像睡著了一般姜挺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上彼硫,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天初家,我揣著相機(jī)與錄音,去河邊找鬼。 笑死溜在,一個(gè)胖子當(dāng)著我的面吹牛陌知,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播掖肋,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼仆葡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了志笼?” 一聲冷哼從身側(cè)響起沿盅,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎纫溃,沒想到半個(gè)月后腰涧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡紊浩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年窖铡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坊谁。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡费彼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出口芍,到底是詐尸還是另有隱情箍铲,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布鬓椭,位于F島的核電站颠猴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏小染。R本人自食惡果不足惜芙粱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望氧映。 院中可真熱鬧春畔,春花似錦、人聲如沸岛都。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽臼疫。三九已至择份,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間烫堤,已是汗流浹背荣赶。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來泰國打工凤价, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拔创。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓利诺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親剩燥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子慢逾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

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

  • 從哪說起呢? 單純講多線程編程真的不知道從哪下嘴灭红。侣滩。 不如我直接引用一個(gè)最簡單的問題,以這個(gè)作為切入點(diǎn)好了 在ma...
    Mr_Baymax閱讀 2,757評(píng)論 1 17
  • Object C中創(chuàng)建線程的方法是什么变擒?如果在主線程中執(zhí)行代碼君珠,方法是什么?如果想延時(shí)執(zhí)行代碼娇斑、方法又是什么策添? 1...
    AlanGe閱讀 1,736評(píng)論 0 17
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,079評(píng)論 25 707
  • 蓬萊仙山長島行, 旖旎飄渺攝人心悠菜。最是黃渤交接處,欲化大鵟嘯天清败富。 三十號(hào)晚七點(diǎn)在牡丹園上的大巴車悔醋,沒有避開如浪的...
    安靜不失文藝的數(shù)據(jù)師閱讀 801評(píng)論 6 6
  • 文/小馬乖乖乖 01 和他在一起已經(jīng)六年了。 我們相識(shí)于網(wǎng)絡(luò)兽叮,約見在嚴(yán)肅而又莊重的人民大禮堂前的一顆大樹下芬骄。 他,...
    小馬乖乖乖閱讀 8,964評(píng)論 6 15