【问题标题】:Does a async function gets added to the callstack?异步函数是否被添加到调用堆栈中?
【发布时间】:2020-10-21 17:17:20
【问题描述】:

如果我声明如下函数:

function a(){
   //do something
}

如果我用a() 执行它,它会被放到调用堆栈的顶部,并在完成时弹出。当函数在调用栈中时,主线程被阻塞并且不能做其他事情,直到调用栈为空。 但是 async 函数究竟会发生什么?

如果我这样做:

async function b() {
   //do something
}

函数返回一个promise并且不会阻塞主线程。这是否意味着这个函数被传递给 Web API 而不是调用堆栈?然后在完成后传递给回调队列,然后传递给调用栈,这样我们就可以执行回调函数了?

b.then(function() {
   //do something after finish
})

【问题讨论】:

  • 可能的答案,请查看stackoverflow.com/questions/56241258/…
  • 您所说的“被传递给 Web API”是什么意思?
  • @Bergi 我的意思是承诺被传递给微任务而不是 Web API。 (我希望是正确的)所以我认为异步函数也被传递给微任务队列?
  • 不,我猜“web API”并不是你想的那样。不,虽然 Promise 回调是通过任务队列调度,但当它们被执行时,它们仍然被放在调用堆栈中。

标签: javascript asynchronous async-await


【解决方案1】:

是的,async functions 在执行期间会像普通函数一样位于调用堆栈中。可以考虑对await关键字进行脱糖:

function log(x) {
  console.log("at "+x);
}
function delay(t) {
  return new Promise(resolve => setTimeout(resolve, t));
}
async function example() {
  log(1);
  await delay(50);
  log(2);
  return 3;
}
function example2() {
  log(1);
  return delay(50).then(function cont() {
    log(2);
    return 3;
  })
}

example() 调用与example2() 调用完全相同。被调用的函数被压入堆栈,调用log(1),后者被压入堆栈并运行,当它返回(从堆栈中弹出)时,它调用delay(50),后者被压入堆栈并运行,依此类推。现在,当await 运算符被计算时,异步函数将接受操作数承诺,使用.then() 调用附加回调,然后从example() 调用返回最终的承诺完成函数体。因此,当它到达await 关键字时,它会从堆栈中弹出,并且调用者可以继续运行同步代码(这通常涉及对返回的承诺执行某些操作)。

然后,当 Promise 被解决时(在上面的例子中,当超时发生时),作业被安排运行附加的 then 回调。他们可能必须等待事件循环变为空闲(调用堆栈变为空),然后当承诺作业开始时,代码再次放入调用堆栈。在example2 中,这是将被调用的cont 函数,在async function 中,它将是example 主体的延续,它在await 处停止。在这两种情况下,它都会调用log(2),它被压入堆栈并运行,当它返回(从堆栈中弹出)时,return 3 语句被执行,该语句解析了承诺并从堆栈中弹出执行上下文,离开它是空的。

【讨论】:

    【解决方案2】:

    它确实进入了调用堆栈,当异步函数运行的时间被移出调用堆栈,直到函数被执行或拒绝。此时它被移动到回调队列中,而所有其他同步函数可以继续在调用堆栈之外执行,然后当调用堆栈为空时,事件循环在回调队列中查找需要执行的任何其他内容。如果回调队列中有任何内容,则事件循环将回调推送到调用堆栈中执行。

    【讨论】:

    • “何时运行异步函数” - 请更具体地说明您的意思,因为当前的措辞有误。
    猜你喜欢
    • 1970-01-01
    • 2013-10-12
    • 1970-01-01
    • 2021-11-21
    • 2016-05-25
    • 1970-01-01
    • 2014-11-24
    • 2017-04-26
    • 1970-01-01
    相关资源
    最近更新 更多