【问题标题】:JS: async function implicitly unwraps promise?JS:异步函数隐含地解开承诺?
【发布时间】:2020-08-28 22:07:14
【问题描述】:

据我所知,异步函数将它们的返回值隐式包装到一个 Promise 中。这对所有属性都有效,除了 Promises 本身。

async function f() {
  return new Promise((res) => {
    setTimeout(() => {
      res("Why am I being unwrapped")
    }, 1000)
  })
}

(async () => {
  console.log(await f())
})()

那些在被退回之前会被打开。这样await f() 实际上就在等待两个嵌套的 Promise。

请注意,这也适用于显式创建的 Promises (Promise.resolve(new Promise(...)))

有没有好的方法来避免这种情况?我真的很想有一个没有像这样的快速修复的嵌套 Promise。

async function y() {
  return {wrapped: new Promise((res) => {
    setTimeout(() => {
      res("Why am I being unwrapped")
    }, 1000)
  })}
}

(async () => {
  console.log((await y()).wrapped)
})()

Sandbox

【问题讨论】:

  • 你想要一个在你调用await 时返回一个Promise 的async 函数吗?目前尚不清楚混乱是什么。
  • developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… - 请注意此部分:If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise.。所以如果返回值已经是一个promise,它不会被包装。
  • 如果你想让一个promise解析为一个promise,那么你必须将内部的promise包装在一个对象中,这样第一个promise就会解析为一个包含promise的对象。无论是Promise.resolve(somePromise),从.then() 处理程序返回一个promise 还是在async 函数中返回一个promise,都是如此。它就是这样设计的。
  • 有趣的是,为什么?它的设计方式是因为您几乎总是希望 Promise 链接起来,并且让父 Promise 等待子 Promise。没有将父子 Promise 链接在一起的用例是什么?
  • 没有。展平适用于每个 thenable(使用 .then 方法的对象)。

标签: javascript node.js asynchronous promise async-await


【解决方案1】:

根据MDN documentation on async functions

异步函数总是返回一个承诺。如果异步函数的返回值不是显式的 Promise,它将被隐式包装在 Promise 中。

换句话说,由于您返回的是 Promise,因此它不会被包装。如果你想重新包装它,你可以,但你也可以去掉 async 具有类似效果的关键字。你会得到原始的 Promise,除非await它。

如此有效:

function f() {
  return new Promise((res) => {
    setTimeout(() => {
      res("Might be wrappers on some of you, but not on me!")
    }, 1000)
  })
}

(async () => {
  console.log(f())
})()

这会给你这样的输出:

Promise { <pending> }

如果目标是拥有一个返回原始 Promise 的 Promise,那么您将与 Promise 系统设计的所有功能作斗争,即在发现 Promise 时自动解包。 Promise.resolve() 包装了所有 Promise,如果你以某种方式给它一个 Promise,它会递归地解包。

您可以通过返回不是 Promise 的东西来做到这一点,就像在您的示例中一样,但这似乎违背了 Promise 的全部目的。

如果您尝试解决的特定问题绝对需要延迟 Promise,请考虑返回一个函数而不是某个任意对象:

function f() {
  return () => {
    return new Promise((res) => {
      setTimeout(() => {
        res("I'll start as soon as you ask.")
      }, 1000)
    })
  };
}

然后,当您想获得实际的 Promise 时,您可以致电 let p = f() 和稍后的 p()。这就是 JavaScript 通常处理延迟执行的方式。

如果你想立即启动 Promise 的执行,你仍然可以适应:

function f() {
  const p = new Promise((res) => {
    setTimeout(() => {
      res("Already underway when requested.")
    }, 1000)
  })

  return () => { p };
}

尽管如果可能的话,通常最好避免使用这些技术,而且通常情况下也是如此。

【讨论】:

  • @JonasWilms 避免这种情况的方法是首先不制造问题。如果您想要一个原始的承诺,请不要使用async 来声明它,这只会违反规定。
  • "我真的很想有一个 nested Promise" ...我认为从问题中可以清楚地看出这与 async / await 无关。
  • @JonasWilms “抱歉,JavaScript 不想那样工作。”我添加了一个注释,说明即使使用 Promise.resolve() 也无济于事,因为它会自动打开它。 Promises 的一切都是为了实现这一目标而设计的。
  • 是的,这就是问题的答案
猜你喜欢
  • 1970-01-01
  • 2018-08-26
  • 2021-11-01
  • 2022-11-01
  • 2022-01-26
  • 2018-09-07
  • 2019-04-13
  • 1970-01-01
  • 2023-04-06
相关资源
最近更新 更多