JavaScript—调用堆栈何时变为“JavaScript”;空的;?

JavaScript—调用堆栈何时变为“JavaScript”;空的;?,javascript,asynchronous,concurrency,settimeout,event-loop,Javascript,Asynchronous,Concurrency,Settimeout,Event Loop,我读过几篇关于事件循环的帖子,根据 当堆栈为空时,将从队列中取出一条消息并 已处理 作为一名JS新手,我仍然困惑的是——调用堆栈究竟何时变为“空”?比如说, <script> function f() { console.log("foo"); setTimeout(g, 0); console.log("foo again"); } function g() { console.log("bar"); } function b() { console.log("

我读过几篇关于事件循环的帖子,根据

当堆栈为空时,将从队列中取出一条消息并 已处理

作为一名JS新手,我仍然困惑的是——调用堆栈究竟何时变为“空”?比如说,

<script>
function f() {
  console.log("foo");
  setTimeout(g, 0);
  console.log("foo again");
}
function g() {
  console.log("bar");
}
function b() {
  console.log("bye");
}

f();
/*<---- Is the stack empty here? */
b();
</script>

函数f(){
控制台日志(“foo”);
设置超时(g,0);
console.log(“又是foo”);
}
函数g(){
控制台日志(“bar”);
}
函数b(){
控制台日志(“再见”);
}
f();

/* 当当前正在执行的Javascript已经完成并且没有更多的顺序指令要执行时,JS引擎将从事件队列中拉出下一项

因此,在您的示例中:

f();
b();
// JS is done executing here so this is where the next item will be
// pulled from the event queue to execute it
Javascript是单线程的,这意味着Javascript的当前线程将一直运行到完成,在一个序列中执行所有指令,直到到达代码末尾。然后,也只有到那时,它才会从事件队列中提取下一项

以下是一些可能有助于您理解的其他答案:

(本文中有一大堆事件循环引用)


我能想到的最好的解释方法是,在代码运行完所有相关路径之前,调用堆栈不会为空。setTimeout为0只会将代码推到堆栈的末尾

当代码在运行时运行时,将运行的所有内容都是调用堆栈的一部分,将根据调用的顺序和调用的任何超时/间隔/异步方法调整顺序

一些例子:

function foo() {
  console.log('foo');
}

function bar() {
  baz();
  console.log('bar');
}

function baz() {
  setTimeout(function() { console.log('timeout') }, 0);
  console.log('baz');
}

foo();
baz();
// call stack ends here, so, timeout is logged last.

// in console
// foo
// baz
// timeout
正如您所看到的,bar不包括在运行时堆栈中,因为它没有被调用。如果我们有一些HTML:

<div onclick="bar()">Bar runs</div>
Bar运行
当您单击该div时,您将看到
baz
bar
,然后
timeout
记录到控制台,因为超时总是被推到当前运行的进程/调用堆栈的末尾


希望这个解释有帮助

最简单的解释是:当前脚本、函数或事件处理程序中的所有同步代码都已运行完毕


直接回答“如果我有数百万行…”是-您的
setTimeout
调用被卡在队列中,将等待轮到它。

尽管
标记中的代码块没有封装在显式函数中,将其视为浏览器通知javascript运行时执行的全局函数可能会有所帮助。因此,在脚本块中的代码执行完毕之前,调用堆栈不会为空。

我能想到的最好的解释方法是,在代码运行完所有相关路径之前,调用堆栈不会为空。setTimeout为0只会将代码推到堆栈的末尾,即:在b()之后。仍然有
b
要执行,因此
f
之后堆栈不会为空。在
b
之后还有什么要做的呢?@Jacques我认为堆栈的概念应该理解为函数调用/帧,而不是如果还有代码要执行。。。我从和中得到了这样的印象…这个视频在这个主题上非常有帮助:简而言之,超时事件在剩余脚本执行之后才会发生。因为全局环境是一个执行环境,我想当脚本最初加载时,它位于调用堆栈的底部。谢谢——在我的示例中,在
f()
完成后,堆栈上到底还有什么内容?b()和任何尚未运行的超时。(在您的示例中,它们都是。)这样想,将要运行的每个语句都在您的调用堆栈中,按照它们在代码中被调用的顺序。唯一不按顺序排列的是超时/间隔/异步方法。“setTimeout为0只会将代码推到堆栈的末尾”-更准确的说法是将代码推到排除队列,而不是调用堆栈-这是两个不同的概念(jfriend00的回答很好地解释了这一点)@Kobi我不想争论语义问题。您所调用的队列是堆栈,堆栈是需要运行的语句的队列。@Kobi的观点是有效的,并且没有错误。事件队列和调用堆栈之间有很大的区别,即事件队列是FIFO,堆栈是LIFO