參考:
阮一峰e(cuò)vent loop
MDN Concurrency model and Event Loop
阮一峰線(xiàn)程和進(jìn)程概念
javascript Event Loop機(jī)制詳解
線(xiàn)程和進(jìn)程
進(jìn)程是指CPU所能處理的單個(gè)任務(wù),任一時(shí)刻,CPU總是運(yùn)行一個(gè)進(jìn)程,而一個(gè)進(jìn)程里可以有幾個(gè)線(xiàn)程,幾個(gè)線(xiàn)程同時(shí)進(jìn)行,協(xié)同完成一個(gè)任務(wù)。
什么是event loop??
事件循環(huán)
簡(jiǎn)單說(shuō),就是在程序中設(shè)置兩個(gè)線(xiàn)程:一個(gè)負(fù)責(zé)程序本身的運(yùn)行,稱(chēng)為”主線(xiàn)程”;另一個(gè)負(fù)責(zé)主線(xiàn)程與其他進(jìn)程(主要是各種I/O操作)的通信,被稱(chēng)為”Event Loop線(xiàn)程”(可以譯為”消息線(xiàn)程”)
像javascript就是基于單線(xiàn)程的運(yùn)行機(jī)制,這種模型與其他語(yǔ)言如c,java就有非常明顯的區(qū)別。
任務(wù)隊(duì)列
javascript的單線(xiàn)程就意味著所有的任務(wù)都需要排隊(duì),前一個(gè)執(zhí)行完后一個(gè)才會(huì)執(zhí)行。

這樣看來(lái),其實(shí)等待的時(shí)間甚至有時(shí)候會(huì)多于執(zhí)行的時(shí)間,造成cpu空等并且后面的事項(xiàng)又很緊急等待的情況。
這時(shí),javascript語(yǔ)言的設(shè)計(jì)者就了解到,可以先掛起正在等待返回的任務(wù),先執(zhí)行后面的任務(wù)

于是,所有任務(wù)就可以分為兩種,同步任務(wù)和異步任務(wù)。同步任務(wù)是指主線(xiàn)程上的任務(wù),只有前一個(gè)任務(wù)執(zhí)行完畢才能執(zhí)行后一個(gè)任務(wù);異步任務(wù)是指不進(jìn)入主線(xiàn)程,進(jìn)入’任務(wù)隊(duì)列‘的任務(wù)。
任務(wù)隊(duì)列中的事件

像這樣的機(jī)制通常運(yùn)用在一些不是可以立即執(zhí)行的,比如用戶(hù)產(chǎn)生的時(shí)間,只要有指定回調(diào)函數(shù),這些事件就會(huì)進(jìn)入“任務(wù)隊(duì)列”,等待主線(xiàn)程完成后進(jìn)行讀取。
任務(wù)隊(duì)列也是先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)。
異步任務(wù)的事件
除此之外,只要是有回調(diào)函數(shù)的事件,就幾乎都是異步事件,會(huì)被放進(jìn)任務(wù)隊(duì)列中。拿setTimeout()事件來(lái)說(shuō),這是最經(jīng)典的事件了。
setTimeout()接收兩個(gè)參數(shù),第一個(gè)是回調(diào)函數(shù),第二個(gè)是延遲時(shí)間
然而,在下面的例子中你會(huì)發(fā)現(xiàn),即使延遲時(shí)間為0,還是相同的結(jié)果,這就是因?yàn)槿蝿?wù)隊(duì)列中的事件需要在主線(xiàn)程完成之后再執(zhí)行,通過(guò)這樣我們也可以看到,如果主線(xiàn)程任務(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
小試題
在一篇帖子看到的,剛開(kāi)始還想不明白ヽ(≧□≦)ノ。
摘錄下來(lái)。
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:剛開(kāi)始不明白的是為什么2是排在6和7的后面,明明bar()里面的setTimeout比直接setTimeout多一個(gè)調(diào)用級(jí)別?
后來(lái)想到,在一次調(diào)用bar()后,進(jìn)入bar()函數(shù)里面,就要把該函數(shù)里的進(jìn)程運(yùn)行結(jié)束才會(huì)返回,所以bar()函數(shù)里面的setTimeout要運(yùn)行結(jié)束才會(huì)出來(lái)繼續(xù)執(zhí)行任務(wù)隊(duì)列里接下來(lái)的其他任務(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聲明的變量存在變量提升的問(wèn)題
2 是因?yàn)閖s單線(xiàn)程所以動(dòng)態(tài)添加事件的事件被掛起,直到主線(xiàn)程結(jié)束
解決的辦法很簡(jiǎn)單,把var改為let,就不存在變量提升的問(wèn)題?;蛘咭部梢詮氖录炱鸬慕嵌瘸霭l(fā),找其他的解決辦法如閉包