【问题标题】:What is the point of the await keyword within an async function in terms of returning a value?就返回值而言,异步函数中的 await 关键字有什么意义?
【发布时间】:2022-01-27 05:54:54
【问题描述】:

我研究了很多帖子、文章和文档。我正在寻求更深入的了解。

来自https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

await 表达式导致 async 函数执行暂停,直到解决 Promise,[...]

let fetchLocation = () => {
  //fetches information from satellite system and 
  //returns a Vector2 of lat long on earth
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({
        lat: 73,
        long: -24
      });
    }, 2000);
  });
}

//async functions automatically return a promise.
let main = async() => {

  //awaits the promises resolution
  let result = await fetchLocation();
  console.log("I'm the awaited promise result", result)

  //immediately returns a promise as promise pending 
  return result;
}

console.log(main().then(res => console.log("I'm the original promise, just passed along by the async function, it didn 't a-wait for me", res)))

在进入这个兔子洞之前,我知道 async/await 是让异步代码看起来同步的语法糖。

因此,我完全期望看到 main 返回 lat long 坐标。相反,它返回一个承诺。

几个问题。

  1. MDN 文档有错吗?它实际上不会暂停执行吗?似乎 await 关键字实际上并没有“暂停”函数执行......调用 main 立即返回一个 Promise<pending> 对象。然而,在它返回后,我们在等待的变量result 中收到了已履行承诺的值。我想这来自事件循环? (并且异步返回语句仍然是同步的。)

  2. 所以看起来 await 解包了一个 Promise,而 async 包装了一个 Promise,但是您需要在 async 中使用 await。我知道await实际上可能是引擎盖下的promise.then(),但是如果他们想让它在外观上完全同步,为什么不让async函数在与await一起使用时返回已解决的promise值?否则 async await 似乎有点多余。

【问题讨论】:

  • 我不确定你到底在提议什么。您想知道为什么 async/await 在不使用这些关键字的情况下的行为与同步代码不完全相同?如果不出意外,这将阻止调用者立即使用await,这是一个非常常见的用例。 await 暂停执行,从“我是等待的承诺结果”日志之前的 2 秒延迟可以看出,但同步调用堆栈代码仍然运行并返回承诺。 Good video 这可能有助于解释为什么您会看到同步代码首先运行。
  • “因此,我完全希望看到 main 返回经纬度坐标。相反,它返回了一个承诺。”async 函数 always 返回一个承诺。
  • 我更想知道这个异步主函数如何显然同时是两件事。在 return 语句的情况下它可以是同步的,这意味着它没有等待。并且还以某种方式异步等待。我正在考虑解释器逐行读取函数。此外,我所提议的更像是不仅仅是在引擎盖下将 a then 链接到返回的 promise 对象,那么是什么让它变得更好?

标签: javascript asynchronous async-await


【解决方案1】:

MDN 文档有错吗?

没有

它实际上并没有暂停执行吗?

它暂停async函数的执行,将Promise返回给调用函数——因为它还没有到达async函数中它知道返回什么的点——然后执行调用函数(以及其余代码)继续。

但是,在它返回后,我们在等待的变量结果中收到已履行的承诺的值。我想这来自事件循环?

当事件循环停止忙碌时(首先运行调用async 函数的代码以及它在此期间拾取的任何其他内容)正在被@的承诺987654324@ed 解析,然后async 函数被唤醒并赋予该函数的解析值以继续处理。

为什么不让 async 函数在与 await 一起使用时返回已解析的 promise 值?

因为这会阻塞整个事件循环,这就是为什么首先在 JS 中引入异步逻辑(从回调样式 API 开始)的原因。

否则异步等待似乎有点多余。

考虑一种情况,您希望从 API 连续请求多个 URL(这样您就不会在 API 上同时发送太多请求):

const data = [];
for (let i = 0; i < urls.length; i++) {
    const response = await fetch(urls[i]);
    const response_data = await response.json();
    data.push(response_data);
}

如果您要在没有await 的情况下重写它,那么您最终可能会得到一些递归来计算您通过循环的方式。它会复杂得多。

async/await 使处理复杂的 Promise 组合变得简单,而简单的 Promise 变得微不足道。

知道 async/await 是使异步代码看起来同步的语法糖。

async 函数内并且在它里面。

【讨论】:

  • 在外部同步执行环境的上下文中查看它更有意义。我的下一个问题是,由于 main 是一个异步函数,但它立即返回继续主线程,它稍后如何唤醒?它不会一返回就被扔出调用堆栈吗?
  • @user14716012 — 不,因为 async 就是这样做的。
  • 是的,但是怎么做?通常当一个函数返回一些东西时,它会从调用堆栈中弹出并被安排进行垃圾收集,除非我猜它包含一个闭包。我很好奇它是如何与异步函数一起工作的。
  • 上面的例子也不能写成``` const data = []; for (let i = 0; i { res.json() .then(res => data.push(result) } } ` `` 我想虽然 await 语法更简洁一些。
  • “是的,但是怎么做?” — 这是一个依赖于特定 JS 引擎的实现细节。
【解决方案2】:

它并没有真正暂停函数,它只是允许您编写函数代码的其余部分,就好像它是一样的。但这实际上只是将函数的其余代码包装成.then()

例如

await foo = someFunc();
console.log(foo);

像这样执行

someFunc().then((foo => {
    console.log(foo);
});

由于它确实还是异步的,所以不能返回解析后的值,因为原来的调用函数是立即返回的。但是如果调用函数也声明为async,它会返回一个Promise,调用者既可以再次使用await(看起来是同步的)也可以使用.then()

【讨论】:

  • 有趣的是它只是链接的语法糖。然后
  • 发现了一些新的东西,主异步函数中await之上的所有代码行都是同步运行的,await之下的所有行(除了return语句)都是异步运行并发送到事件循环跨度>
  • 这基本上就是.then() 所做的。
  • "await 和promises 的根本区别在于await 暂停当前函数的执行,而promise.then() 在回调链中加入X 调用后继续执行当前函数。"
  • 这是为了简化情况,从程序员的角度描述它是如何工作的,而不是它在幕后的实际实现方式。
猜你喜欢
  • 2020-03-01
  • 2020-03-23
  • 1970-01-01
  • 2020-12-23
  • 2021-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-06
相关资源
最近更新 更多