【问题标题】:Does the v8 engine first fill up the call stack and then run the code? or does it run the code as soon as it enters a functionv8引擎是先填满调用栈再运行代码吗?还是一进入函数就运行代码
【发布时间】:2021-02-26 19:53:51
【问题描述】:

这是我尝试过的代码:

// **Creating variables and functions**
let number_object = { num: 0 }

function doHomework(callback) {     //Starts doing the homework, supposed to finish after 5 seconds
    console.log("writing")

    setTimeout(() => {
        callback()
    }, 5000)
}

function Finished() {        //Called when 5 seconds are over
    console.log("Finished the homework")
}

function loop_(num_object) {  
    ++num_object.num;
    let i = 0;
    for (i = 0; i < 3000000000; ++i) { }
    console.log(`loop number ${num_object.num} over`)
}

// **Calling functions now**
doHomework(Finished)

let i = 0;
for (i = 0; i < 10000000000; ++i) { }  //This takes much more than 5 seconds
console.log(`This took more than 5 seconds, yet "finished" is not going to appear next`)

loop_(number_object)
loop_(number_object)
loop_(number_object)
loop_(number_object)
loop_(number_object)
loop_(number_object)
loop_(number_object)
loop_(number_object)
loop_(number_object)
loop_(number_object)

输出是:

writing
This took more than 5 seconds, yet "finished" is not going to appear next
loop number 1 over
loop number 2 over
loop number 3 over
loop number 4 over
loop number 5 over
loop number 6 over
loop number 7 over
loop number 8 over
loop number 9 over
loop number 10 over
Finished the homework

所以我认为输出表明,代码首先将调用堆栈中的所有函数排队,然后运行它们。这就是为什么所有 10 个函数都在调用堆栈中排队,所以只有在它们完成后,才会出现“完成”

【问题讨论】:

标签: javascript node.js asynchronous callback


【解决方案1】:

这在再次进入事件循环之前被称为运行到完成。

代码首先将调用堆栈中的所有函数排队,然后运行它们。这就是为什么所有 10 个函数都在调用堆栈中排队的原因

不,这 10 个函数没有在调用堆栈上排队。如果有的话,它们会在程序代码中“排队”。只是您的无数增量操作没有在任何地方排队。它们被编码在程序中,然后运行。

【讨论】:

  • 好的,所以在运行之前,代码知道“我的第一个工作是完成代码中存在的函数”,无论回调什么,都会在前面的函数之后完成?跨度>
  • 是的。它甚至与功能无关,只是“完成此代码”,包括例如你的顶级for 循环。
【解决方案2】:

重要的是要记住setTimeout 确实调用您传递给它的函数。相反,它保存了一个指向函数的指针,当超时到期时,javascript 稍后将调用该函数。

从这个意义上说,javascript 与 Java、C/C++、Pascal、Python 等没有什么不同。在所有这些语言中,如果您将函数指针保存到链接/列表或数组,该函数将在您稍后调用它之前不会被调用.唯一的区别是带有回调的 API 是内置在 javascript 中的标准库的一部分。确保此类 API 也存在于其他语言中,尤其是在 GUI 编程方面。

就调用堆栈而言,javascript 的行为通常与 C/C++ 完全相同,但在涉及闭包时确实会变得很奇怪。使用闭包,javascript 的调用堆栈的行为类似于 Lisp/Scheme 或 Haskell 或 Swift 或 Python。

事件循环

请务必注意,javascript 是单线程的(有些人可能会声称并非如此 - 他们是错误的)。解释器的结构一般如下:

// Pseudocode

script_to_execute = compile_scripts();
event_handlers = [];

do {
    scheduled_callbacks = execute(script_to_execute)

    for each (callback in scheduled_callbacks) {
        event_handlers.push(callback)
    }

    event = wait_for_async_events()

    for each (callback in event_handlers) {
        if (callback.event == event) {
            script_to_execute = callback.script
        }
    }

}
while (event_handlers.length > 0)

所以基本上您对setTimeout 的调用会将这些函数添加到事件处理程序列表中。您的整个脚本最初位于script_to_execute。如果您查看上面的程序流程,应该很明显,只要您的脚本仍在执行,就无法处理任何事件。因此,javascript 中的所有异步操作,包括setTimeout、HTTP 请求、DOM 更新等,都只会在脚本的最后一行之后发生。

您也可以看到,所有这些都与调用堆栈或范围无关。这只是时机——有些事情只是发生在未来而不是现在。

【讨论】:

  • C++ 和 Java 也有闭包,它们中的任何一个的调用堆栈都没有什么特别之处。另一方面,Haskell 的调用堆栈是一个非常不同的野兽,但更多是因为使用 thunk 的惰性求值而不是因为闭包。
  • "javascript 是单线程的(其他说法是错误的)" - even in the browser, it's not。但是javascript 作为一种语言既不是“单线程”也不是“多线程”。虽然 ECMAScript 没有为线程指定任何内置函数,但它也不排除实现它们的实现,并且有 运行时在多个线程上运行 javascript。
  • @Bergi 虽然 ECMAScript 没有指定单线程行为 WHATWG 和 W3C 在处理 setTimeoutfetch 等问题时​​会做什么。
  • 对于setTimeoutfetch等异步的东西,他们指定了一个事件循环,是的。对于 web worker、SharedArrayBuffers 和 atomics 之类的东西,它们指定了一个多线程环境。
猜你喜欢
  • 2021-04-18
  • 2016-02-26
  • 1970-01-01
  • 1970-01-01
  • 2014-07-09
  • 2013-01-26
  • 2017-09-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多