【问题标题】:Is there a practical advantage to returning a pending promise? [duplicate]返回一个未决的承诺有实际优势吗? [复制]
【发布时间】:2019-03-09 02:19:26
【问题描述】:

在很大程度上,我想我了解异步函数在 JavaScript / Node.js 中是如何工作的,并且我熟悉 async/await 的语法。但这个问题是关于一个非常具体的案例,我似乎在任何地方都找不到,而且我认为它涉及await的内部运作,这超出了我的理解。

首先,我将从一个简单的示例开始,然后将其扩展为真正的工作代码。 (注意:我实际上使用的是 Typescript,所以你会看到 void 而不是 undefined,但这对问题并不重要。)

async function subtask() {
    // doSomeStuff();
    return; // returns a `Promise<void>`
}
// async function run() {
//  see options below
// }
function app() {
    run().catch((e) => console.error(e));
}

选项 1

async function run() {
    await subtask(); // resolves to `void`
    return; // returns a new `Promise<void>`
}

选项 2

async function run() {
    return subtask(); // returns a new Promise, resolving to the `Promise<void>` of `subtask()`
}

在上面的简单示例中,我有一个异步函数run,它调用了一个较小的异步函数subtask。这两个函数都必须返回一个Promise&lt;void&gt;。我有两个选择:(1)等待较小的函数并返回一个新的Promise&lt;void&gt;,或者(2)返回一个由较小的函数给出的包装承诺,稍后将解析为void

我对它的工作原理缺乏了解。在选项 1 中,是否在 subtask() 返回之前暂停执行?这实际上是什么意思?这是否意味着异步subtask 同步执行?这是否意味着调用run()app 也会暂停执行?如果app 是异步的,会有什么不同吗?

让 Promise 冒泡并稍后解决,还是直接在 run 函数中解决它是“更好”(性能更高)吗?

这很重要的原因是,在我的实际代码中,我有一堆较小的子任务,它们都返回 void,然后大函数也必须返回 void——它不能返回数组。 (请注意,子任务不需要以任何特定顺序运行。)

选项 1

async function run() {
    await Promise.all([
        subtask0(),
        subtask1(),
        subtask2(),
    ]);
    return;
}

选项 2

async function run() {
    return Promise.all([
        subtask0(),
        subtask1(),
        subtask2(),
    ]).then((_all) => {});
}

function app() {
    // do some stuff
    run(); // if `run` contains `await`, does execution pause here?
           // what if `app` was async?
    // do some more stuff
}

【问题讨论】:

  • 选项 2 不需要使用异步,因为它已经返回了一个承诺
  • @Igor - 我的理解是用async 声明的函数将返回一个承诺。如果我需要一个函数来返回一个承诺,为什么我不能在上面声明async?不管是不是awaiting 什么?
  • 你在那里所做的(通过添加 async 而不是调用 await)正在向调用者返回一个包装在一个 Promise 中的 Promise。
  • 关于性能,取决于您为此构建的环境,可能会被打字稿填充到一些生成器怪物中。检查打字稿游乐场上的实际结果代码以查看差异。我认为在功能上它们都非常相似,尽管 async 在返回的 Promise 中捕获异常。

标签: javascript node.js asynchronous async-await


【解决方案1】:

Promise 冒泡总是更好。这避免了创建一个额外的 Promise 对象,该对象也将被等待(不过,在您使用的任何 JavaScript 引擎中,这是否在幕后进行了优化仍有待商榷)。

async function run() {
    await subtask();
    return; 
}

这会创建一个额外的Promise(随后将在链中执行一个额外的回调)。

async function run() {
    return subtask(); 
}

这实际上并没有按照您的想法进行。这也创建了一个额外的Promise(因为您使用的是async 关键字),并且在功能上与前面的示例几乎相同。通过使用async 关键字,您将创建并返回一个新的Promise,它将解析/拒绝与调用subtask() 创建的Promise 相同的值。如果您删除 async 关键字,那么这将避免创建额外不必要的 Promise

现在有了您的 Promise.all() 示例,我认为它们都是最佳的(假设您从第二个中删除了不必要的 async 关键字,如上所述)。第一个将创建 2 个 PromisePromise.all() 一个,以及从 async 函数返回创建的一个),第二个也将创建(Promise.all() 一个,以及通过调用 @ 创建的一个987654341@)。无论您想使用哪个,基本上都取决于个人选择。就个人而言,我喜欢第二个示例,因为它没有混合使用 async 函数和 Promises,我认为这更容易理解。

对于您问题的最后一部分,只要存在 await 关键字(因此在 run() 调用中),就会暂停执行。

您也可以从本质上考虑使用 await 关键字来转换您的代码:

// Do some stuff
let result = await run();
// Do some other stuff

到这里:

// Do some stuff
run().then(result => {
    // Do some other stuff
};

await 的重点是减少嵌套引起的混乱/复杂性)

【讨论】:

猜你喜欢
  • 2016-07-20
  • 2021-12-24
  • 2015-11-27
  • 2015-12-16
  • 2015-06-05
  • 1970-01-01
  • 1970-01-01
  • 2020-05-12
  • 2021-05-08
相关资源
最近更新 更多