本篇内容参考连结
[第十六週] JavaScript 进阶:事件迴圈 Event Loop、Stack、Queue
[笔记] 理解 JavaScript 中的事件循环、堆叠、伫列和併发模式(Learn event loop, stack, queue, and concurrency mode of JavaScript in depth)
Philip Roberts | JSConf EU
Event Loop
Data Structure
在準备了解Event Loop前, 必须先回忆一下一些资料结构
Stack 堆叠 : 原则是 LIFO ("Last in, First Out"), 最后进去的先出来.Queue 伫列 : 原则是 FIFO ("First in, First Out"), 所谓先进先出.Definition
由于JS Engine是单执行绪(Single thread), 只能执行同步(synchronous)事件, 并一次执行一个任务, 所以需要一个机制来处理非同步(asynchronous)的事件. 而这个机制的集中一个环节就叫做 Event Loop, 用来决定执行任务的顺序.
Call Stack
是用来记录程式目前执行到程式哪个部分, 当进入某一个函式, 便会把这个函式添加(push)到最上方, 直到函式return或结束则会从堆叠抽离(pop).
参考以下範例
function b() { console.log("b finished");}function a() { b(); console.log("a finished");}a();
结果会得到
b finished
a finished
当我们去执行的时候, 首先进入stack的是档案中全域环境的程式(这里称作 main()), 如下
main()
接着往下执行, a() 首先被呼叫, 此时会把a()函式堆叠在上面, 并先进入a()函式做执行, 而在a()中, b()又被呼叫,又做了一次堆叠在最上面, 最后call stack 会长的如下
b();
a();
main();
之后跑到 console.log("b finished");
后, b()执行完毕, 并会从call stack中抽离
a();
main();
以此类推, a()执行完,也会从 call stack中抽离, 最后回到 main()中继续逐行执行直到结束, 然后main()也抽离.
Call Stack - 补充
Stack Overflow : 资源是有限的, 当程式一直堆叠的时候, 例如函式不断呼叫自己呈现无限迴圈如下, 程式进入a()之后就一直不断重複堆叠, 到最后会出现错误 (Maximum call stack size exceeded), 也就是 Stack Overflow.function a() { a();}a();
Blocking : 当执行一段程式需要等待很长的时间, 像是网页卡住的感觉, 就叫做阻塞 Blocking. 像是把非同步 ajax request 请求变成同步(Synchronous), 堆叠时就逼须等request拿到资料后, 并从stack中pop出去才可以往下执行.Callback Queue
当有非同步操作的时候, 像是setTimeout, 浏览器会先丢一个timer到Web API, 等时间到了后, 就会把timer中的 Callback function 丢进Callback Queue. 再藉由Event Loop的机制, 等待Call stack净空后, 就会开始执行.
console.log("First");setTimeout(function(){ console.log("Second");},5000);console.log("Third");
执行结果为
"First"
"Third"
"Second"
先来看call stack的变化, 首先会先放入 main(), 之后执行console.log("First");
. 接下来执行到setTimeout这个非同步函式, 浏览器丢一个timer到Web API, 在此同时 setTimeout 就算执行完了, 也从stack抽离, 并不会阻塞整个程式五秒, 所以下一个console.log("Third");
就会接着执行.
再来看callback queue, 当在Web API的timer时间到了后, 会把setTimeout的callback丢入callback queue. 等最后一个main()也抽离并净空后,才会依序把callback丢回 call stack, 并开始执行 console.log("Second");
.
Event Loop
在一个一个了解架构后, 终于回到Event Loop, 其作用就是监控堆叠(call stack)和伫列(callback queue),当堆叠当中没有执行项目的时候, 便把伫列中的内容拉到堆叠中去执行.
Event Loop 的监测顺序
监控call stack监控callback queue如果call stack为空, 且call queue有pending callback把callback queue里的程式, 依序丢入call stack去执行回到步骤一强调 : 第一优先永远是call stack, 当call stack没净空时, 即使非同步的事件处理完, 还是需要等到call stack都执行完, callback queue才会被执行到