Event loop
想要理解同步与非同步可以观看这个影片 : What the heck is the event loop anyway? | Philip Roberts | JSConf EU非常清楚的讲解的同步与非同步。
Stock
在程式的执行里有个东西叫做 "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 overflow指的是stack太多东西满出来了,例如连续呼叫一个function十万次,stack没办法存放这么多动作,就会丢出stack overflow错误。
thread
虽然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 大师吧!