这就需要引入我们今天要讲解的:Event-Loop。
在我们开始之前,先假设要讲解的 Event-loop 是运行在浏览器中的,不是在 Node 或者其它环境里。
我们来介绍一下这篇文章的主角们:
The call stack
Call Stack 是在内存中,用来追踪函数的执行顺序。一个函数会叠加在另一个函数之上,最先添加的函数会最后执行(先进后出)。
The web API
Web API 不是 JS 的一部分,它只是一些可以被 JS 程序使用的方法,例如:setTimeout 、alert 等。
The message queue
message queue 是一个消息的列表,等待与它们相关的函数一起执行。每当被监听的事件触发时,一个新的消息就会被添加到这个列表里。
The event loop
event loop 是一个一直在检测 call stack 是否为空的进程。若 call stack 为空,它就会从 message queue 中取出第一个然后推入 call stack 中执行。
下图是简易浏览器环境:
一个 JS 的故事
让我们看一下,下面的代码以及运行结果:
function foo() {
console.log("One");
setTimeout(() => {
console.log("Two");
}, 0);
console.log("Three");
}
foo();
调用 foo 函数,看下结果:
> One
> Three
> Two
现在我们来看看主角们如何执行上面的代码。
首先,浏览器向 call stack 发送 foo() 函数:
console.log('One') 语句被推送到 call stack 的顶部:
同时,event loop 也会检查 call stack 是否为空:
JS 的运行时会执行栈顶的代码然后移除它:
继续向下执行,浏览器向 call stack 推送了 setTimeout() 语句:
event loop 再次检查:
浏览器会设置一个 timer 用来触发 setTimeout 里面的 callback :
接着后面的语句会被推送到 call stack:
event loop 又来了:
event loop 发现 call stack 并没有空所以它什么也不会干,继续向下执行直到 foo() 函数的结尾:
我们来看看 web API 那里为 setTimeout 设置的 timer,现在 timer 结束了。浏览器把对应的 callback 推送到 message queue。
伴随着最后一条语句的执行然后被清除,foo() 函数里面没有可执行的代码了,它也会被 call stack 移出。
现在,event loop 是个幸运儿:
接下来,event loop 会检测 message queue 里是否有消息:
之后会发送消息相关的函数到 call stack:
最后,JS 运行时执行最后的语句,之后从 call stack 里面移出: