常规 Event Loop
这篇文章展示了给定的 JS 片段的调用堆栈,事件循环,和浏览器环境给出的异步 API 三者之间的关系和执行顺序。
---------------------------------------------------------------------
给出的 JS :
setTimeout(() => {
console.log('hi')
}, 1000)调用堆栈,事件循环和 Web APIs 存在如下关系:
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | | | | |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |起初,这三者都是空的。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | <global> | | | |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |代码开始执行,并将一个 <global> 推进 Call Stack。注:<global> 可以理解为全局变量所处的执行环境。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
> setTimeout(() => { | <global> | | | |
console.log('hi') | setTimeout | | | |
}, 1000) | | | | |
| | | | |第一行代码执行完毕,将会把函数执行作为第二项推进 Call Stack。值得注意的是 Call Stack 是一个堆栈,它遵循后进先出原则--推进去的最后一个元素将会成为弹出的第一个元素。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
> setTimeout(() => { | <global> | | | timeout, 1000 |
console.log('hi') | setTimeout | | | |
}, 1000) | | | | |
| | | | |执行 setTimeout 函数实际上调用了 JS 之外的代码。setTimeout 属于 Web API 的一部分,而 Web API 是由浏览器环境提供的。当然了,node 中也存在一套不同的 API。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | <global> | | | timeout, 1000 |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |然后,setTimeout 语句执行完毕,它将工作交付 Web API 。Web API 将会等待指定的时长(1000ms)。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | | | | timeout, 1000 |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |此时,这里没有其他的 JS 需要执行了,Call Stack 现在是空的状态。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | | function <-----timeout, 1000 |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |一旦时延到达指定时长(1000ms),Web API 将会把代码加入到 Event Loop (事件循环)内,从而使得 JS 能够执行。
这里需要注意的是 Web API 并非直接将所要执行的代码推到 Call Stack中,因为这样做可能打断正在执行的代码,那会得到一个非常诡异的结果。
Event Loop 是一个队列。第一个被推进来的元素也会被首先推出去。遵从先入先出原则。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | function <---function | | |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |当 Call Stack 内没有代码执行的时候, JS 执行环境将检查 Event Loop 内是否有等待执行的任务在排队。如果有,第一个元素将会被移动到 Call Stack 来执行。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | function | | | |
> console.log('hi') | console.log | | | |
}, 1000) | | | | |
| | | | |箭头函数执行的结果就是调用了 console.log 。那么这句代码将被推到 Call Stack 内。(因为这是同步的API)
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | function | | | |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |
> hiconsole.log 执行完毕后,打印出 hi。Call Stack 同时移除这部分代码。
[code] | [call stack] | [Event Loop] | | [Web APIs] |
--------------------|-------------------|--------------| |---------------|
setTimeout(() => { | | | | |
console.log('hi') | | | | |
}, 1000) | | | | |
| | | | |
> hi最后,箭头函数内没有其他的指令需要执行,它也被从 Call Stack 中移除。
我们的程序就此结束了运行。
End.
---------------------------------------------------------------------原文链接:Regular Event Loop
有任何建议或疑惑,欢迎在评论区留言。