Javascript的異步編程的理解

在單線程的javascript編程來(lái)說(shuō),所有的任務(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í)行。

具體來(lái)說(shuō)就是:

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è)過(guò)程會(huì)不斷重復(fù)

Event Loop 是上面的第三步 這個(gè)過(guò)程是不斷重復(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í)很長(zhǎng),有可能要等很久,

所以并沒(méi)有辦法保證,回調(diào)函數(shù)一定會(huì)在setTimeout()指定的時(shí)間執(zhí)行。

javascript寫手如果稱一個(gè)函數(shù)為"異步的",其意思是這個(gè)函數(shù)會(huì)導(dǎo)致將來(lái)再運(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) //無(wú)限彈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ā)回來(lái)的狀態(tài),不過(guò)時(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)求回來(lái)觸發(fā)事件監(jiān)聽的標(biāo)志")

console.log("異步任務(wù)ajax請(qǐng)求回來(lái)觸發(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é)尾

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容