參考:
阮一峰e(cuò)vent loop
MDN Concurrency model and Event Loop
阮一峰線程和進(jìn)程概念
javascript Event Loop機(jī)制詳解
線程和進(jìn)程
進(jìn)程是指CPU所能處理的單個(gè)任務(wù)分瘾,任一時(shí)刻偎谁,CPU總是運(yùn)行一個(gè)進(jìn)程,而一個(gè)進(jìn)程里可以有幾個(gè)線程邑时,幾個(gè)線程同時(shí)進(jìn)行粱侣,協(xié)同完成一個(gè)任務(wù)汛闸。
什么是event loop核无?琉用?
事件循環(huán)
簡(jiǎn)單說舷蟀,就是在程序中設(shè)置兩個(gè)線程:一個(gè)負(fù)責(zé)程序本身的運(yùn)行楚昭,稱為”主線程”剂公;另一個(gè)負(fù)責(zé)主線程與其他進(jìn)程(主要是各種I/O操作)的通信沫浆,被稱為”Event Loop線程”(可以譯為”消息線程”)
像javascript就是基于單線程的運(yùn)行機(jī)制情妖,這種模型與其他語言如c吮廉,java就有非常明顯的區(qū)別曲聂。
任務(wù)隊(duì)列
javascript的單線程就意味著所有的任務(wù)都需要排隊(duì)递惋,前一個(gè)執(zhí)行完后一個(gè)才會(huì)執(zhí)行柔滔。
這樣看來,其實(shí)等待的時(shí)間甚至有時(shí)候會(huì)多于執(zhí)行的時(shí)間萍虽,造成cpu空等并且后面的事項(xiàng)又很緊急等待的情況睛廊。
這時(shí),javascript語言的設(shè)計(jì)者就了解到杉编,可以先掛起正在等待返回的任務(wù)超全,先執(zhí)行后面的任務(wù)
于是,所有任務(wù)就可以分為兩種邓馒,同步任務(wù)和異步任務(wù)嘶朱。同步任務(wù)是指主線程上的任務(wù),只有前一個(gè)任務(wù)執(zhí)行完畢才能執(zhí)行后一個(gè)任務(wù)光酣;異步任務(wù)是指不進(jìn)入主線程疏遏,進(jìn)入’任務(wù)隊(duì)列‘的任務(wù)。
任務(wù)隊(duì)列中的事件
像這樣的機(jī)制通常運(yùn)用在一些不是可以立即執(zhí)行的,比如用戶產(chǎn)生的時(shí)間财异,只要有指定回調(diào)函數(shù)倘零,這些事件就會(huì)進(jìn)入“任務(wù)隊(duì)列”,等待主線程完成后進(jìn)行讀取戳寸。
任務(wù)隊(duì)列也是先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)呈驶。
異步任務(wù)的事件
除此之外,只要是有回調(diào)函數(shù)的事件疫鹊,就幾乎都是異步事件袖瞻,會(huì)被放進(jìn)任務(wù)隊(duì)列中。拿setTimeout()事件來說拆吆,這是最經(jīng)典的事件了聋迎。
setTimeout()接收兩個(gè)參數(shù),第一個(gè)是回調(diào)函數(shù)锈拨,第二個(gè)是延遲時(shí)間
然而砌庄,在下面的例子中你會(huì)發(fā)現(xiàn)羹唠,即使延遲時(shí)間為0奕枢,還是相同的結(jié)果,這就是因?yàn)槿蝿?wù)隊(duì)列中的事件需要在主線程完成之后再執(zhí)行佩微,通過這樣我們也可以看到缝彬,如果主線程任務(wù)很多,定時(shí)器的延遲時(shí)間比較短哺眯,就會(huì)出現(xiàn)不準(zhǔn)的情況谷浅。
var m = 1;
var n = 2;
setTimeout(function() {
console.log(m);
}, 0);
console.log(n);
//2
//1
小試題
在一篇帖子看到的,剛開始還想不明白ヽ(≧□≦)ノ奶卓。
摘錄下來一疯。
function foo() {
console.log('1');
bar();
setTimeout(function() {
console.log('2');
}, 0);
Promise.resolve().then(function() {
console.log('3');
Promise.resolve().then(function() {
console.log('4');
});
});
console.log('5');
}
function bar() {
setTimeout(function() {
console.log('6');
}, 0);
setTimeout(function() {
console.log('7');
}, 0);
}
foo();
哈哈哈公布答案
Q:剛開始不明白的是為什么2是排在6和7的后面,明明bar()里面的setTimeout比直接setTimeout多一個(gè)調(diào)用級(jí)別夺姑?
后來想到墩邀,在一次調(diào)用bar()后,進(jìn)入bar()函數(shù)里面盏浙,就要把該函數(shù)里的進(jìn)程運(yùn)行結(jié)束才會(huì)返回眉睹,所以bar()函數(shù)里面的setTimeout要運(yùn)行結(jié)束才會(huì)出來繼續(xù)執(zhí)行任務(wù)隊(duì)列里接下來的其他任務(wù)。
-
2 一個(gè)for循環(huán)添加事件
function send(argument) { console.log(argument); } var m = document.getElementById('ok'); for (var i = 0; i < 5; i++) { console.log(i); //0废膘,1竹海,2,3丐黄,4 m.addEventListener('click', function () { console.log(i) //5斋配,5,5,5艰争,5 }) }
這里看到不一樣的輸出結(jié)果十偶,這有兩方面的原因
1 是因?yàn)関ar聲明的變量存在變量提升的問題
2 是因?yàn)閖s單線程所以動(dòng)態(tài)添加事件的事件被掛起,直到主線程結(jié)束
解決的辦法很簡(jiǎn)單园细,把var改為let惦积,就不存在變量提升的問題∶推担或者也可以從事件掛起的角度出發(fā)狮崩,找其他的解決辦法如閉包