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