使用异步/等待功能时,调用堆栈的行为如何?
const asyncFuntion=async()=>{
//some asynchronous code
}
const first = async()=>{
await asyncFuntion();
console.log('first completed');
debugger;
}
const second = ()=>{
console.log('second completed');
debugger;
}
function main(){
first();
second();
}
main();
在上面的代码中,当在second()中遇到第一个断点时,我可以看到调用堆栈包含main()和second()。在first()的第二个断点期间,调用堆栈包含main()和first()。
在第一个断点期间first()发生了什么。它推到哪里了?假设asyncFunction()需要一些时间才能完成。
请有人帮忙。
答案 0 :(得分:1)
首先,当您到达在second
中命中的断点时,first
已经执行并且不再在堆栈中。
进入first
时,我们立即击中await asyncFunction()
。这告诉JavaScript调用asyncFunction,然后在我们等待其完成时随意去寻找其他事情。 Javascript是做什么的?
首先,我们必须整理asyncFunction
。我们将该函数调用放入事件循环。也就是说,我们将其放入“有时间的事情要做”的队列中。完成后,我们将返回。
现在,我们需要找到其他与空闲时间有关的东西。 JavaScript无法继续first
的下一行(即console.log('firstcomplete')),因为我们的await
意味着我们要等到{{ 1}}完成了。
因此,我们查找堆栈。 Javascript发现asyncFunction
是从main调用的,first
本身不是 first
。换句话说,我们告诉Javascript,首先是否异步并不重要,只要继续即可。因此,我们直接跳至await
。一旦执行了second
,我们就会回头看那个东西,并按照每个人期望的方式继续执行。
然后,在将来的某个时候,我们的second
完成。也许它正在等待API调用返回。也许它正在等待数据库答复。无论等待什么,信号都会发送出去,并且随时可以处理。它不是从asyncFunction
中“调用”的-内部是像回调一样被拾取的,但至关重要的是使用一个全新的堆栈进行调用,一旦我们完成了对函数其余部分的调用,它将被销毁
鉴于我们位于新堆栈中,并且离开了“主”堆栈框架已有很长时间,main
和main
如何在碰到内部的断点时又再次出现在堆栈中它吗?
很长一段时间,如果您在调试器中运行代码,简单的答案就是它们不会。您只需获得所使用的功能,调试器就会告诉您它是从“异步代码”或类似名称中调用的。
但是,如今,某些调试器可以按照等待的代码返回其正在解决的承诺(请记住,first
和await
大多是在承诺之上的语法糖)。换句话说,当您等待的代码完成并且幕后的“承诺”解决了时,调试器将帮助您确定堆栈“应该”是什么样。它显示的内容实际上与引擎最终调用该函数的方式不太相似-毕竟,它是从事件循环中调用的。但是,我认为这是一个有用的补充,它使我们所有人都可以使代码的思维模型比实际发生的事情简单得多!
关于它是如何工作的一些进一步的阅读,其中涵盖了比我这里更多的细节: