Javascript v8引擎是否先填满调用堆栈,然后运行代码?或者,它是在输入函数后立即运行代码吗
以下是我尝试的代码:Javascript v8引擎是否先填满调用堆栈,然后运行代码?或者,它是在输入函数后立即运行代码吗,javascript,node.js,asynchronous,callback,Javascript,Node.js,Asynchronous,Callback,以下是我尝试的代码: // **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(() => {
// **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)
因此,我认为输出表明,代码首先将调用堆栈中的所有函数排队,然后运行它们。这就是为什么所有10个函数都在调用堆栈中排队,因此只有在它们完成后,“finished”(完成)才会出现,这称为再次进入事件循环之前的run to completion(运行到完成) 代码首先将调用堆栈中的所有函数排队,然后运行它们。这就是为什么所有10个函数都在调用堆栈中排队的原因
不,这10个函数没有在调用堆栈上排队。它们在程序代码中“排队”,如果有的话。只是你的大量增量操作没有在任何地方排队。它们在程序中编码,然后运行。重要的是要记住,
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\u to\u execute
中。如果查看上面的程序流,很明显,只要脚本仍在执行,就不会处理任何事件。因此,javascript中的所有异步操作,包括setTimeout
、HTTP请求、DOM更新等,都只会在脚本的最后一行之后发生
您还可以看到,所有这些都与调用堆栈或作用域无关。这只是时间安排——有些事情只是发生在未来而不是现在。这是否回答了你的问题?Ty@Seblor我可能需要经历一段时间,因为我没有听说过微任务和宏任务。会让你知道你也可以阅读关于微任务的文档:虽然StackOverflow对我链接的问题的回答应该涵盖你的情况。@Seblor同时,我不是有意粗鲁,但是,你能告诉我首先发生了什么吗?对不起,我没有太多时间,但是如果你不明白接下来会发生什么,我今天晚些时候可以写一个答案,所以在运行之前,代码知道“我的第一项工作是完成代码中的函数”,并且无论调用什么,都会在前面的函数之后完成?是的。它甚至不只是关于函数,只是“完成这个代码”,包括例如<
>
循环。C++和java也有闭包,对它们中的任何一个调用栈来说都没有什么特别之处。另一方面,Haskell的调用堆栈是一个非常不同的野兽,但更多的是因为thunks的惰性计算,而不是闭包。但是javascript作为一种语言既不是“单线程”也不是“多线程”。虽然ECMAScript没有为线程指定任何内置项,但它也不会阻止实现它们,并且存在在多个线程上运行javascript的运行时。@Bergi而ECMAScript没有指定单线程行为,而W3C和TwG在处理setTimeout
之类的事情时会做什么,fetch
等。对于setTimeout
、fetch
和其他异步内容,它们指定了一个事件循环,是的。对于web workers、SharedArrayBuffer
s和atomics等,它们指定了多线程环境。
// 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)