【问题标题】:Using `Async` keyword while returning a promise object返回承诺对象时使用`Async`关键字
【发布时间】:2020-05-05 02:52:52
【问题描述】:

我正在开发一些 Nodejs 脚本,我有一个关于 JS 本身使用 async 关键字的问题。

例如,假设我有一个返回如下承诺的函数:

function myFunction () {
  ... some job
  return new Promise(async (resolve, reject) => {
    const innerResult = await someHeavyJob();
    ... some job
    resolve(innerResultThatIsManipulated);
  });
}

我可以用另一种方式来写它:

async function myFunction () {
  ... some job
  const innerResult = await someHeavyJob();
  ... some job
  return innerResultThatIsManipulated;
}

但我有一个问题,如果我选择第一个解决方案,那么我是否可以使用 async 关键字,是否推荐?例如,像这样:

async function myFunction () {
  ... some job
  return new Promise(async (resolve, reject) => {
    const innerResult = await someHeavyJob();
    ... some job
    resolve(innerResultThatIsManipulated);
  });
}

强调这是一个异步方法,并且在其代码的所有路径中都返回承诺(不是简单的对象)。

那么,推荐的使用方法是什么?

【问题讨论】:

  • 在你的第一个函数中你试图返回另一个承诺的承诺,有不必要的冗余
  • 异步是一种 Promise(语法糖)。所以你可以使用你个人喜欢的东西。
  • 第一种方式被认为是一种反模式,因为你将一个 promise 返回函数包装在一个 promise 构造函数中。 Promise 构造函数用于将非 Promise 返回的异步函数转换为 Promise

标签: javascript promise async-await


【解决方案1】:

MDN 说:

异步函数声明定义了一个异步函数——a 返回 AsyncFunction 对象的函数。异步函数 通过事件以与其余代码不同的顺序运行 循环,返回一个隐式 Promise 作为其结果。但是语法和 使用异步函数的代码结构看起来像标准 同步函数

通过这样做:

async function myFunction () {
  return new Promise(async (resolve, reject) => {
    const innerResult = await someHeavyJob();
    resolve(innerResult);
  });
}

你正在返回一个隐含的 Promise,它返回一个 Promise。你问过让代码更具可读性是否是一种有趣的方法,我相信它带来的困惑多于澄清。

(甚至不谈论异步函数内部的承诺中的异步函数,实际上只包含只包含该异步函数的承诺,我说的第一个不是最后一个,我是指第二个,你跟着了吗?编辑:你实际上已经编辑了那个 sn-p,它只是为了示例)

MDN 补充:

async/await 的目的是简化同步使用 Promise

在我的理解中,异步函数是一个替代的承诺,而不是一个扩展。因此,按照您建议的方式将两者结合起来似乎并不明智。

【讨论】:

    【解决方案2】:

    这是一个反模式:

    return new Promise(async (resolve, reject) => {
      const innerResult = await someHeavyJob();
      // ... some job
      resolve(innerResultThatIsManipulated);
    });
    

    当构造函数回调要自己创建一个 Promise 然后调用 resolve 并使用(操纵的)承诺结果时,您不应该使用 new Promise 创建一个新的 Promise。

    这真的应该这样编码:

    return someHeavyJob().then((innerResult) =>
      // ... some job
      return innerResultThatIsManipulated;
    });
    

    区别

    所以在上面的评论之后,你的问题实际上是关于在这个函数定义中添加 async 关键字:

    function myFunction () {
      // ... some job 1
      return someHeavyJob().then((innerResult) =>
        // ... some job 2
        return innerResultThatIsManipulated;
      });
    }
    

    以下是主要区别:

    • 如果在调用.then() 之前 引发异常——所以在“某些工作1”中或在调用someHeavyJob 时——那么async 版本仍将返回一个承诺(具有拒绝状态),而非async 版本将引发该错误。

    • 另外,async 版本返回的承诺不是return 语句中返回的承诺。 async 返回的是一个单独的承诺,其分辨率(计划)绑定到您提供给return 语句的那个​​。

    您可以通过像这样编写非async 版本来消除这种差异:

    function myFunction () {
      return Promise.resolve().then(() => {
        // ... some job 1
        return someHeavyJob().then((innerResult) =>
          // ... some job 2
          return innerResultThatIsManipulated;
        });
      });
    }
    

    或者,更好的是,扁平化为一条链:

    function myFunction () {
      return Promise.resolve().then(() => {
        // ... some job 1
        return someHeavyJob();
      }).then((innerResult) =>
        // ... some job 2
        return innerResultThatIsManipulated;
      });
    }
    

    这更好地模仿了async 版本的作用:确保返回一个承诺。

    因此,总而言之,仅添加 async 并不能生成保证以相同方式运行的函数。如果您的目标是始终返回一个承诺,即使在函数执行时发生错误,那么一定要选择async。但是一旦你这样做了,通常也有充分的理由使用await 语法:它只会使代码更具可读性。

    【讨论】:

    • 感谢您的完整回答。非常有用。
    【解决方案3】:

    在您展示的所有示例中,只有第二个是正确的。其背后的原因是 Promise 是一个构造函数(因此您将它与 new 一起使用),它接受一个常规函数,而不是另一个 Promise 作为它的参数。

    示例 1

    解释器将你的第一个例子翻译成这样的东西(理论上):

    new Promise( new Promise((resolve, reject) => { ... }))
    

    这不是你想要的,也被认为是反模式。相反,你想要这个:

    new Promise((resolve, reject) => { ... })
    

    此外,您似乎已经有一个函数 (someHeavyJob) 会导致 Promise。为什么要把它包装在另一个Promise 中?只需使用someHeavyJob 返回的Promise.then 方法来转换它给你的任何东西。

    最后,你会得到类似的东西:

    function myFunction() {
      // ... some job
      return someHeavyJob().
        then( result => {
          // ... some job with result
        })
    }
    

    示例 3

    从技术和逻辑的角度来看,第三个示例不正确的原因与示例 1 不正确的原因相同。

    一般经验法则

    不要使用asyncawait(仅)作为与其他人(或您未来的自己)交流某些功能本质上异步的手段。这两个关键字对您编写的代码都有隐含的影响,并使其行为不同。

    尝试遵守这两种约定中的任何一种(使用Promise使用async/await)。仅在您确实需要时才混合它们。

    就我个人而言,我尝试坚持使用Promise 而不是async/await,因为它们经常把人们搞砸,而且——对我来说——除了让代码“看起来更好”之外,它们并没有带来太多好处”。不过,这只是我个人的看法。

    【讨论】:

      猜你喜欢
      • 2020-01-04
      • 2021-11-27
      • 2016-05-20
      • 1970-01-01
      相关资源
      最近更新 更多