先举一个简单的例子
const bar = () => console.log('bar')
const baz = () => console.log('baz')
const foo = () => {
console.log('foo')
bar()
baz()
}
foo()
当运行此代码时,会先调用foo(),然后调用log函数,然后调用bar(),最后调用baz(),其函数调用栈如下所示。
每次迭代中的事件循环都会查看调用堆栈中是否有东西并执行它直到调用堆栈为空:
接下来的实例看看我们如何将函数延迟到最后直到堆栈被清空,增减一个定时器调用bar(),并且指示0秒之后运行。
const bar = () => console.log('bar')
const baz = () => console.log('baz')
const foo = () => {
console.log('foo')
setTimeout(() => {
bar();
}, 0);
baz()
}
foo()
此时的函数调用栈如下所示:
这是程序中所有函数的执行顺序
马老师,发生甚么事了?
当调用 setTimeout() 时,浏览器或 Node.js 会启动定时器。 当定时器到期时(在此示例中会立即到期,因为将超时值设为 0),则回调函数会被放入“消息队列”中。
在消息队列中,用户触发的事件(如单击或键盘事件、或获取响应)也会在此排队,然后代码才有机会对其作出反应。 类似 onLoad 这样的 DOM 事件也如此。
事件循环会赋予调用堆栈优先级,它首先处理在调用堆栈中找到的所有东西,一旦其中没有任何东西,便开始处理消息队列中的东西。
我们不必等待诸如 setTimeout、fetch、或其他的函数来完成它们自身的工作,因为它们是由浏览器提供的,并且位于它们自身的线程中。 例如,如果将 setTimeout 的超时设置为 2 秒,但不必等待 2 秒,等待发生在其他地方。