想要理解同步與非同步可以觀看這個影片 : What the heck is the event loop anyway? | Philip Roberts | JSConf EU非常清楚的講解的同步與非同步。
在程式的執行裡有個東西叫做 "call stack" 負責記錄著每個function執行時所需要用到的資源並記錄著function執行的順序
Example :
function a()
{
return 1;
}
function b()
{
a();
}
function c()
{
b();
}
c();
先呼叫的function C, C 裡面呼叫function b, b 裡面呼叫 function a
知名的錯誤stack overflow指的是stack太多東西滿出來了,例如連續呼叫一個function十萬次,stack沒辦法存放這麼多動作,就會丟出stack overflow錯誤。
雖然JaveScrip只有一個"thread" 代表著一次只能做一件事,那怎麼進行非同步動作呢?雖然一個thread只能做一件事,但是卻可以有多個thread,以setTimeout function來說 :
setTimeout(fn,2000);
當2秒後呼叫fu function,瀏覽器就會開啟另一個thread去計時,而main thread就可以往下執行下一個指令,當另一個thread計數好2秒後會將fn丟回main thread。
(圖片來源 : Understanding Event Loop, Call Stack, Event & Job Queue in Javascript)
若執行了 "setTimeOut(fn,2000)"這一行程式,會先把setTimeout(fn,2000)放到stock中,由於setTimeout屬於Web API,所以會將這行程式丟到另一個thread去計數2秒,然後當計數結束後會將fn放到callback queue中,而當stack清空時(其他function執行完畢),callback queue會將計數完成的fn放回stack
callback queue : 不斷偵測stack是否為空,若是空的就把callback queue裡面的東西丟到stack中
以程式角度來說 :
while(1)
{
if(callStack.length === 0 && callbackQueue.length > 0)
{
callStack.push(callbackQueue.dequeue()) // 拿出 callbackQueue ,並放到 callStack
}
}
舉一個影片中的範例 :
setTimeout(() => {
console.log("0ms")
},0)
console.log("hello");
在這邊由於"setTimeout"會將計數放到另一個thread中進行計數,雖然計數的時間為0ms,但是由於進到另一個thread中,所以0ms計數完後會被放到callback queue中,而這個時候stack裡面擁有"console.log("hello")" (stack並非為空),所以callback queue會等待"console.log("hello")"執行結束後才會將callback queue中的"console.log("0ms")" 放入stack中並且執行,所以會先是hello先被console出來後,0ms才會被console出來。
Step 0 :
Step 1 : 將setTimeout放入stack中,之後將他放到Web API中計數
Step 2 : 將"console.log("hello")"放入stack,Web API中計數完成放入callback queue
由於stack中還有動作(console.log("hello"))所以callback queue中的console.log("0ms")不會放到stack中。
Step 3 : Stack中為空,將callbakc queue中的console.log("0ms")放入stack並執行
參考資料 :
JavaScript 中的同步與非同步(上):先成為 callback 大師吧!