本文介绍了使用异步/等待时调用堆栈的工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用异步/等待功能时,调用堆栈的行为如何?

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()需要一些时间才能完成.

请有人帮忙.

解决方案

首先,当您到达在second中命中的断点时,first已经执行并且不再在堆栈中.

进入first时,我们立即击中await asyncFunction().这告诉JavaScript调用asyncFunction,然后在我们等待其完成时随意去寻找其他事情. Javascript是做什么的?

首先,我们必须整理asyncFunction.我们将该函数调用放入事件循环中.也就是说,我们将其放入有时间时要做的事情"的队列中.完成后,我们将返回.

现在,我们需要找到其他与空闲时间有关的东西. JavaScript无法继续first的下一行(即console.log('firstcomplete')),因为我们的await意味着我们不能在asyncFunction完成之前继续进行下一行.

因此,我们查找堆栈. Javascript发现first是从main调用的,而first本身不是 await.换句话说,我们告诉Javascript,首先是否异步并不重要,只要继续即可.因此,我们直接跳到second.一旦执行了second,我们便会回过头来查看该内容,并按照每个人期望的方式继续执行.

然后,在将来的某个时候,我们的asyncFunction完成.也许它正在等待API调用返回.也许它正在等待数据库答复.无论等待什么,信号都会发送出去,并且随时可以处理.它不是从main中调用"的-内部会像回调一样来拾取该函数,但至关重要的是使用一个全新的堆栈进行调用,一旦我们完成了对函数的其余部分的调用,该堆栈将被销毁./p>

鉴于我们位于一个新的堆栈中,并且离开了主"堆栈框架已经很久了,当我们击中其中的断点时,mainfirst又如何再次出现在堆栈中? >

很长一段时间,如果您在调试器中运行代码,简单的答案就是它们不会.您只需获得所使用的功能,调试器就会告诉您它是从异步代码"或类似的代码中调用的.

但是,如今,某些调试器可以按照等待的代码返回其正在解决的承诺(请记住,awaitasync主要是 只是在promise之上的语法糖).换句话说,当您等待的代码完成并且幕后的承诺"解决时,您的调试器会帮助您确定堆栈应该"是什么样子.它显示的内容实际上与引擎最终调用该函数的方式不太相似-毕竟,它是从事件循环中调用的.但是,我认为这是一个有用的补充,它使我们所有人都可以使代码的思维模型比实际发生的事情简单得多!

关于它是如何工作的一些进一步的阅读,其中涵盖了比我这里更多的细节:

How does the Call Stack behave when async/await functions are used ?

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();

In the above code, when the first breakpoint is encountered in second(), I could see that the call stack contained main() and second(). And during the second breakpoint in first(), the call stack contained main() and first().

What happened to first() during the first breakpoint. Where is it pushed ? Assuming that the asyncFunction() takes some time to complete.

Someone please help.

解决方案

First off, when you get to the breakpoint you hit in second, first has already executed and is no longer on the stack.

When we go into first, we instantly hit an await asyncFunction(). This tells JavaScript to call the asyncFunction, then feel free to go looking for something else to do while we're waiting for it to finish. What does Javascript do?

Well, first of all, we have to sort out asyncFunction. We throw that function call into the event loop. That is to say, we put it in a queue of "things to do when we have time". We'll come back to it when it's finished.

Now we need to go find something else to do with our free time. JavaScript can't continue with the next line of first (ie, the console.log('first completed')) because our await means we can't carry on the next line until the asyncFunction has finished.

So, we look up the stack. Javascript sees that first was called from main, and first itself wasn't awaited. In other words, we told Javascript it doesn't matter if first is async, just carry on regardless. So, we skip right on to second. Once we've executed second, we look back to whatever called that, and carry on with execution in the way everyone would expect.

Then, at some point in the future, our asyncFunction finishes. Maybe it was waiting on an API call to return. Maybe it was waiting on the database to reply back to it. Whatever it was waiting for, the signal got sent, and it's ready to be dealt with. It's not "called" from main - internally the function is picked back up, somewhat like a callback, but crucially is called with a completely new stack, which will be destroyed once we're done calling the remainder of the function.

Given that we're in a new stack, and have long since left the 'main' stack frame, how do main and first end up on the stack again when we hit the breakpoint inside it?

For a long time, if you ran your code inside debuggers, the simple answer was that they wouldn't. You'd just get the function you were in, and the debugger would tell you it had been called from "asynchronous code", or something similar.

However, nowadays some debuggers can follow awaited code back to the promise that it is resolving (remember, await and async are mostly just syntactic sugar on top of promises). In other words, when your awaited code finishes, and the "promise" under the hood resolves, your debugger helpfully figures out what the stack "should" look like. What it shows doesn't actually bear much resemblence to how the engine ended up calling the function - after all, it was called out of the event loop. However, I think it's a helpful addition, enabling us all to keep the mental model of our code much simpler than what's actually going on!

Some further reading on how this works, which covers much more detail than I can here:

这篇关于使用异步/等待时调用堆栈的工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-05 11:15
查看更多