在 JavaScript 中,异步函数到底什么时候返回?

问题描述 投票:0回答:2

我发现如果不仔细思考的话,这可能会很棘手。异步函数到底什么时候返回给调用者?

我故意不将其制作成片段,以便读者可以猜测输出是什么:

打印出来的内容:

async function foo() {
  console.log("COMING INTO foo");

  await new Promise((resolve) => {
    setTimeout(resolve, 3000);
  });
  console.log("Right before return");
  return 123;
}

function takeIt(fn) {
  console.log("STARTING");
  foo();
  console.log("Function returned");
}

takeIt(foo);

为什么?

视角一可以是:嗯,我们都知道异步函数会暂停

setTimeout
并休眠 3 秒,并且稍后不会返回...所以

STARTING
COMING INTO foo
Right before return
Function returned

视角二可以是:嗯,

foo()
返回123(或者不返回任何东西,这意味着返回
undefined
),这是一个承诺......返回承诺是瞬时的,所以它是:

STARTING
Function returned
COMING INTO foo
Right before return

现在,如果我们在绘图中再添加一项调整:

async function foo() {
  await Promise.resolve(678);

  console.log("COMING INTO foo");

  await new Promise((resolve) => {
    setTimeout(resolve, 3000);
  });
  console.log("Right before return");
  return 123;
}

function takeIt(fn) {
  console.log("STARTING");
  foo();
  console.log("Function returned");
}

takeIt(foo);

并且可以有更多的观点......在我运行程序并得到正确答案之前,我猜测了所有 3 个案例打印的内容。但我并不知道它到底是如何运作的,但我做了一个猜测。有人可以详细说明它是如何工作的吗?如果需要,我会在几天后发布我的答案,说明我认为它到底是如何工作的。

问题是:

foo()
什么时候返回,确切地说,它是如何工作的?指导原则是什么?

javascript asynchronous async-await
2个回答
3
投票

一旦异步函数的代码运行到

Function returned
,异步函数就会将控制流返回给其调用者(即,在示例中记录
await
)。在此之前它不会产生回流。所以这里:

async function foo() {
  console.log("COMING INTO foo");

  await new Promise((resolve) => {
    setTimeout(resolve, 3000);
  });

调用

foo
将立即记录
COMING INTO foo
,然后由于
await
,控制流返回给
foo
的调用者。

同样:

async function foo() {
  await Promise.resolve(678);

  console.log("COMING INTO foo");

await
首先出现,因此在这里,控制流在
foo
记录任何内容之前返回。

这里,“控制流被收回”与“向调用者返回 Promise”同义。


-1
投票

我的答案是,有时很难准确说出异步函数的行为方式,这里是转换:

async function foo() {
  console.log("COMING INTO foo");

  await new Promise((resolve) => {
    setTimeout(resolve, 3000);
  });
  console.log("Right before return");
  return 123;
}

function takeIt(fn) {
  console.log("STARTING");
  foo();
  console.log("Function returned");
}

takeIt(foo);

对于上面的代码,完全可以看成:

function foo() {
  // any code before the first await goes here, which is executed immediately:
  console.log("COMING INTO foo");

  // when the first await is encountered, the whole thing becomes a promise
  // and is returned to the caller:
  return new Promise((resolve0, reject0) => {
    const promise0 = new Promise((resolve) => {
      setTimeout(resolve, 3000);
    });

    promise0.then(v0 => { 
      // the more await there are in the original code, the more "nested"
      // `then` there will be. It does indeed become a nesting hell
      console.log("Right before return");

      // no matter how deep the nesting is, the last returned value
      // is called with resolve0
      resolve0(123);
    });
  });
}

所以想法是:一些代码将立即同步执行,但在

await
的第一个点,它突然变成一个巨大的承诺,其最终解析值将是异步函数的最后一行。所以这个“巨大的promise”就像漂浮在空中,等待着被调用和再次调用(我们可以把它想象成处理程序和处理程序被一次又一次地调用,并且是深深嵌套的)。

在async函数中,所有从await中获取值的变量就好像它们在同一作用域中一样,但是在转换后的代码中,then处理程序可以使用外部作用域访问这些变量,因此当then handler 被调用时,它利用闭包来访问这些外部作用域变量。

这个问答的目的是为了让我们准确地了解正在发生的事情,因为我发现一些程序员,包括我自己,在了解这一点之前,对正在发生的事情有一个模糊的想法,但无法真正说出,并且在没有真正理解的情况下编写代码这可能不是一个好的做法。

© www.soinside.com 2019 - 2024. All rights reserved.