1、报错:超出最大调用堆栈大小
function foo() {
foo()
}
foo()
2、正常执行:堆栈不会溢出
function foo() {
setTimeout(foo,0)
}
foo()
步骤
- 调用
foo()会将foo函数放入调用堆栈(call stack)。 - 在处理内部代码时,JS引擎遇到
setTimeout。 - 然后将
foo回调函数传递给WebAPIs(箭头1)并从函数返回,调用堆栈再次为空 - 计时器被设置为0,因此
foo将被发送到任务队列(箭头2)。 - 由于调用堆栈是空的,事件循环将选择
foo回调并将其推入调用堆栈进行处理。 - 进程再次重复,堆栈不会溢出。
运行示意图如下所示:
3、浏览器无法响应
function foo() {
return Promise.resolve().then(foo);
}
foo()
JavaScript中有宏任务和微任务。setTimeout回调是宏任务,而Promise回调是微任务。
主要的区别在于他们的执行方式。宏任务在单个循环周期中一次一个地推入堆栈,但是微任务队列总是在执行后返回到事件循环之前清空。因此,如果你以处理条目的速度向这个队列添加条目,那么你就永远在处理微任务。只有当微任务队列为空时,事件循环才会重新渲染页面
每次调用’foo‘都会继续在微任务队列上添加另一个’foo‘回调,因此事件循环无法继续处理其他事件(滚动,单击等),直到该队列完全清空为止。 因此,它会阻止渲染。