JavaScript - 调用堆栈确实变为"为空"?

时间:2016-01-10 03:58:31

标签: javascript asynchronous concurrency settimeout event-loop

我已经在事件循环中阅读了多个帖子/ SO帖子,并根据MDN's article

  

当堆栈为空时,会从队列中取出一条消息   处理。

作为JS的新手,我仍然感到困惑的是 - 当调用堆栈确实变为"空的时候#34;?例如,

<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>

正确的执行顺序是foo - foo again - bye - bar

但是今天我开始思考:退出f()电话后,技术上堆栈是不是很空?我的意思是在那一点上我们不在任何函数内部,并且我们还没有开始任何新的执行,所以不应该处理setTimeout调用消息(已经立即排队) ,然后转到b(),并提供foo - foo again - bar - bye的订单?

如果我们要执行a million lines of code或一些密集计算并且setTimeout(func, 0)只是在队列中待了多长时间怎么办?

4 个答案:

答案 0 :(得分:8)

尽管<script>标记中的代码块未包含在显式函数中,但将其视为浏览器告知javascript运行时执行的全局函数会有所帮助。因此,在脚本块中的代码执行完毕之前,调用堆栈不为空。

答案 1 :(得分:4)

当正在执行的当前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的当前线程运行完成,执行序列中的所有指令,直到它到达代码的末尾。然后,只有这样,它才会从事件队列中提取下一个项目。

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

How Javascript Timers Work

How does JavaScript handle AJAX responses in the background?(这篇文章中的一大堆事件循环引用)

Do I need to be concerned with race conditions with asynchronous Javascript?

Can JS event handlers interrupt execution of another handler?

答案 2 :(得分:2)

我能想到解释它的最好方法是,在代码完成所有相关路径的运行之前,调用堆栈不为空。 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>

当您单击该div时,您将看到bazbar,然后timeout已登录到控制台,因为超时始终被推送到当前正在运行的进程的末尾/调用堆栈。

希望这个解释有所帮助!

答案 3 :(得分:2)

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

直接回答“如果我有数百万行...”是的 - 你的setTimeout来电已停留在队列中并等待轮到。