在單線程的javascript編程來說,所有的任務(wù)分為兩種,一種是同步任務(wù)(synchronous),
另一種是異步任務(wù)(asynchronous)。同步任務(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ù)才會(huì)進(jìn)入主線程執(zhí)行瓦糟。
具體來說就是:
1.所有的同步任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧(execution context stack)
2.主線程之外,還存在一個(gè)"任務(wù)隊(duì)列"(task queue),只要異步任務(wù)有了運(yùn)行結(jié)果,就在"任務(wù)隊(duì)列"之中放置一個(gè)事件
3.一旦"執(zhí)行棧"中的所有同步任務(wù)執(zhí)行完畢,系統(tǒng)就會(huì)讀取"任務(wù)隊(duì)列",看看里面有哪些事件筒愚。
那些對(duì)應(yīng)的異步任務(wù),于是結(jié)束等待狀態(tài),進(jìn)入執(zhí)行棧,開始執(zhí)行
4.主線程不斷重復(fù)上面的第三步
只要主線程空了,就會(huì)讀取"任務(wù)隊(duì)列",這就是javascript的運(yùn)行機(jī)制,這個(gè)過程會(huì)不斷重復(fù)
Event Loop 是上面的第三步 這個(gè)過程是不斷重復(fù)的
可以參考:
http://www.ruanyifeng.com/blog/2014/10/event-loop.html
setTimeout(function(){console.log(1);}, 0);
console.log(2);
上面代碼的執(zhí)行結(jié)果總是2,1,因?yàn)橹挥性趫?zhí)行完第二行以后,
系統(tǒng)才會(huì)去執(zhí)行"任務(wù)隊(duì)列"中的回調(diào)函數(shù)
需要注意的是菩浙,setTimeout()只是將事件插入了"任務(wù)隊(duì)列"巢掺,必須等到當(dāng)前代碼(執(zhí)行棧)執(zhí)行完,
主線程才會(huì)去執(zhí)行它指定的回調(diào)函數(shù)劲蜻。要是當(dāng)前代碼耗時(shí)很長陆淀,有可能要等很久,
所以并沒有辦法保證先嬉,回調(diào)函數(shù)一定會(huì)在setTimeout()指定的時(shí)間執(zhí)行轧苫。
javascript寫手如果稱一個(gè)函數(shù)為"異步的",其意思是這個(gè)函數(shù)會(huì)導(dǎo)致將來再運(yùn)行另一個(gè)函數(shù)
后者取自于事件隊(duì)列(若后面這個(gè)函數(shù)是作為參數(shù)傳遞給前者的,則成為前者的回調(diào)函數(shù))
理解了異步任務(wù)的任務(wù)隊(duì)列后:
demo1:
var ajaxRequest=new XMLHttpRequest();
ajaxRequest.open("GET","data/activity.json",true)
ajaxRequest.send(null) ? ? ?
while(ajaxRequest.readyState ===1){ //當(dāng)有發(fā)出open請(qǐng)求 readyState為1
alert(1) //無限彈1,當(dāng)然永遠(yuǎn)也輪不懂send運(yùn)行
}
//ajaxRequest.readyState ===4也是不行的 因?yàn)楫?dāng)readyState===4時(shí),while已經(jīng)執(zhí)行完了
//所以需要引入事件監(jiān)聽(例如onreadystatechange)把異步的任務(wù)返回主線程的任務(wù)隊(duì)列
//ajaxRequest.send(null) //異步任務(wù)在同步任務(wù)全部完成后執(zhí)行 ? 即send真正的執(zhí)行位置
readyState,onreadystatechange都是要依賴異步的send函數(shù)發(fā)回來的狀態(tài),不過時(shí)間微秒級(jí)
onreadystatechange對(duì)應(yīng)的函數(shù)相當(dāng)于send的回調(diào)函數(shù),要在send執(zhí)行后才能執(zhí)行
(這就是事件監(jiān)聽的用處:返回任務(wù)隊(duì)列)
demo2:
var a=10;
var ajaxRequest=new XMLHttpRequest();
ajaxRequest.open("GET","data/activity.json",true)
ajaxRequest.send(null)
ajaxRequest.onreadystatechange=function(){? //異步事件隊(duì)列返同步任務(wù)對(duì)的事件監(jiān)聽
console.log("我是ajax請(qǐng)求回來觸發(fā)事件監(jiān)聽的標(biāo)志")
console.log("異步任務(wù)ajax請(qǐng)求回來觸發(fā)事件監(jiān)聽的時(shí)刻",new Date().getTime()-c)
}
setTimeout(function(){
console.log("同步任務(wù)執(zhí)行完0.004秒后立刻執(zhí)行,setTimeout函數(shù)實(shí)際的延時(shí)時(shí)間會(huì)有偏差")
console.log("異步任務(wù)setTimeout執(zhí)行的時(shí)刻",new Date().getTime()-c)
a++
console.log("異步隊(duì)列返回同步隊(duì)列,a="+a);
},0)
console.log("同步任務(wù)","a="+a)
var four = new Date();
var c = four.getTime()
for(var i=0;i<1000000000;i++){
if(i === 0){
console.log("同步任務(wù),i=0時(shí)執(zhí)行的時(shí)刻:",new Date().getTime()-c)
}
if(i === (1000000000-1)){
console.log("同步任務(wù),i=999999時(shí)執(zhí)行的時(shí)刻:",new Date().getTime()-c)
console.log("ajax的send()是在我執(zhí)行完它才開始請(qǐng)求的,并不是我在進(jìn)行的同時(shí)它也在請(qǐng)求,所以send()函數(shù)放在腳本的結(jié)尾和現(xiàn)在是一樣的效果")
}
}
//ajaxRequest.send(null) ? ? ? ? ? //實(shí)際上異步的send函數(shù)是在主線程空了再執(zhí)行 ?也就是腳本的結(jié)尾