使用异步/等待与承诺的区别?

Posted

技术标签:

【中文标题】使用异步/等待与承诺的区别?【英文标题】:Difference of using async / await vs promises? 【发布时间】:2019-04-03 01:41:09 【问题描述】:

我正在寻找关于在我的 nodeJS 应用程序中使用什么的答案。

我有处理我对 mssql 的通用 dB 访问的代码。这段代码是使用 async 函数编写的,然后我使用 promise 调用该函数并且一切正常。

随着我的应用程序越来越大,代码越来越大,我计划将一些逻辑移动到函数中,然后调用它们。

所以我的问题是:混合使用 async/await 和 Promise 有什么缺点,还是真的没关系?

Async / await 使编写更具可读性的代码变得更容易,因为我必须在返回某些内容之前读取和写入多个数据库,并且我需要其中一些结果。

那么问题是什么是更好的方法? 已设置且无法更改的 dB 层上的异步/等待 逻辑层 async / await 这将允许我在函数调用上进行 async / 和 await ,或者如果我对逻辑使用 promise,那么我在函数调用时会被 promise 卡住。

所以我希望有人能给我更多的见解,如果一个人比另一个人有更多的优势,除了能够编写更清晰的代码。

【问题讨论】:

这篇文章强调了 async/await 相对于 promises 的一些优势...hackernoon.com/… - 但我很想知道使用一种方法是否有任何性能改进... @Alqbal 与 async/await 相比没有性能改进。 async/await 仅适用于承诺,因此没有async/await OR 承诺,只有async/await AND 承诺。所以,你真的是在问你是只使用简单的承诺还是使用 async/await 的承诺。 我们可以清理一些重要的东西吗? async / await 都是关于承诺的,所以这个问题的措辞是误导性的。 await 语句等待承诺被履行。您真正要问的是使用 awaitthen() 方法。简短的回答是,这主要是品味问题,因为他们做同样的工作。 【参考方案1】:

async/await 和 promises 密切相关。 async 函数返回承诺,await 是等待承诺解决的语法糖。

混合使用 Promise 和 async 函数的唯一缺点可能是代码的可读性和可维护性,但您当然可以将异步函数的返回值用作 Promise,以及将 await 用于返回的常规函数一个承诺。

您是否选择其中一个主要取决于可用性(您的 node.js / 浏览器是否支持async?)和您的审美偏好,但一个好的经验法则(基于我当时的偏好写作)可能是:

如果需要串行运行异步代码:考虑使用async/await

return asyncFunction()
.then(result => f1(result))
.then(result2 => f2(result2));

const result = await asyncFunction();
const result2 = await f1(result);
return await f2(result2);

如果你需要嵌套的 Promise:使用 async/await:

return asyncFunction()
.then(result => 
  return f1(result)
  .then(result2 => f2(result, result2);
)

const result = await asyncFunction();
const result2 = await f1(result);
return await f2(result, result2);

如果您需要并行运行:使用 Promise。

return Promise.all(arrayOfIDs.map(id => asyncFn(id)))

建议您可以在表达式中使用await 来等待多个任务,如下所示:*注意,这仍然是从左到右依次等待,如果您不这样做也可以期待错误。否则,由于fail fast behaviour 的Promise.all() 导致行为不同

const [r1, r2, r3] = [await task1, await task2, await task3];

(async function() 
  function t1(t) 
    console.time(`task $t`);
    console.log(`start task $t`);
    return new Promise((resolve, reject) => 
      setTimeout(() => 
        console.timeEnd(`task $t`);
        resolve();
      , t);
    )
  

  console.log('Create Promises');
  const task1 = t1(100);
  const task2 = t1(200);
  const task3 = t1(10);

  console.log('Await for each task');
  const [r1, r2, r3] = [await task1, await task2, await task3];

  console.log('Done');
())

但与Promise.all 一样,并行promise 需要在出现错误时得到妥善处理。你可以阅读更多关于here的信息。

注意不要将前面的代码与以下代码混淆:

let [r1, r2] = [await t1(100), await t2(200)];

function t1(t) 
  console.time(`task $t`);
  console.log(`start task $t`);
  return new Promise((resolve, reject) => 
    setTimeout(() => 
      console.timeEnd(`task $t`);
      resolve();
    , t);
  )

console.log('Promise');
Promise.all([t1(100), t1(200), t1(10)]).then(async() => 

  console.log('Await');
  let [r1, r2, r3] = [await t1(100), await t1(200), await t1(10)]
);

使用这两种方法是不等价的。 Read more about the difference.

最后,Promise.all 是一种更简洁的方法,可以更好地扩展到任意数量的任务。

【讨论】:

实际上你可以使用 await let [r1, r2] = [await task1, await task2] 并行运行。看***.com/questions/35612428/… @Qwerty 感谢您的反馈!我不确定这是否正确。检查我的编辑和链接的答案。那有意义吗?如果我弄错了,请告诉我! @lucascaro 我有疑问。与使用 then 相比,await 不会使代码阻塞吗? 某种意义上,你可以这么想,await“暂停”当前函数,直到 promise 被解决,但是由于这个函数是异步的,它不会阻塞任何外部的当前函数。 您的代码没有反映@Qwerty 的建议(如果您点击链接会更清楚)。 task1task2 是 Promise,因此它们已经在并行运行。在使用await 之前,您需要先调用每个t1(),而不是[await t1(100), await t1(200), await t1(10)]。一个冗长的方式是let task1 = t1(100); let task2 = t1(200); let task3 = t1(10); let [r1, r2, r3] = [await task1, await task2, await task3];。但是在 async/await 代码中使用 Promise.all 是有意义的,因为 await 只需要 Promise: let [r1, r2, r3] = await Promise.all([t1(100), t1(200), t1(10)]);【参考方案2】:

其实这取决于你的节点版本,但如果你可以使用async/await,那么你的代码将更具可读性和更易于维护。 当您将函数定义为“异步”时,它会返回本机 Promise,当您使用 await 调用它时,它会执行 Promise.then。

注意: 将您的 await 调用放在 try/catch 中,因为如果 Promise 失败,它会发出 'catch',您可以在 catch 块中处理它。

try
let res1 = await your-async-function(parameters);
let res2 = await your-promise-function(parameters);
await your-async-or-promise-function(parameters);

catch(ex)
// your error handler goes here
// error is caused by any of your called functions which fails its promise
// this methods breaks your call chain

你也可以像这样处理你的'catch':

let result = await your-asyncFunction(parameters).catch((error)=>//your error handler goes here);

提到的这个方法不会产生异常,所以继续执行。

我认为async/await 除了原生 Promise 模块实现之外没有任何性能差异。

我建议使用bluebird 模块而不是node 内置的原生promise。

【讨论】:

【参考方案3】:

此时使用 Promises 的唯一原因是使用 Promise.all() 调用多个异步作业,否则通常使用 async/await 或 Observables 会更好。

【讨论】:

您可以使用Promise.all()async/awaitconst [p1, p2, p3] = await Promise.all([p1(), p2(), p3()]); p1,p2,p3 都是该实例中的经典承诺。如果您单独等待它们中的每一个,它们将被同步执行。 它们都同时执行。在调用下一个之前,我们不会等待一个完成。我们只是等待所有人完成Promise.all() 没错。但是如果你调用 await p1()、await p2、await p3,它们将是并发的。 p1-p3 必须声明为明确的承诺。 如果这个答案能解释 why如何 使用 async/await 或 Observables 会更好。正如所写的那样,对于尚未熟悉这些主题的人来说,这似乎不是很有帮助。【参考方案4】:

这取决于你擅长什么方法,promise 和 async/await 都很好,但是如果你想写异步代码,使用同步代码结构你应该使用 async/await 方法。就像下面的例子,一个函数返回具有 Promise 或 async/await 风格的用户。 如果我们使用 Promise:

function getFirstUser() 
    return getUsers().then(function(users) 
        return users[0].name;
    ).catch(function(err) 
        return 
          name: 'default user'
        ;
    );

如果我们使用 aysnc/await

async function getFirstUser() 
    try 
        let users = await getUsers();
        return users[0].name;
     catch (err) 
        return 
            name: 'default user'
        ;
    

在 promise 方法中,我们需要一个 thenable 结构来遵循,而在 async/await 方法中,我们使用 'await' 来保持异步函数的执行。

您可以查看此链接以获得更清晰的信息访问https://medium.com/@bluepnume/learn-about-promises-before-you-start-using-async-await-eb148164a9c8

【讨论】:

【参考方案5】:

昨天我做了一个初步决定,从使用 Promises 切换到使用 Async/Await,独立于 nodejs,基于访问 Promise 链中先前值的困难。我确实提出了一个紧凑的解决方案,使用 'bind' 将值保存在 'then' 函数中,但 Async 在允许直接访问局部变量和参数方面似乎更好(而且确实如此)。当然,Async/Await 更明显的优势是消除了令人分心的显式“then”函数,取而代之的是看起来很像普通函数调用的线性表示法。

但是,我今天的阅读发现了 Async/Await 的问题,这使我的决定偏离了方向。我想我会坚持使用 Promises(可能使用宏预处理器使“then”函数看起来更简单),直到 Async/Await 得到修复,几年后。

这是我发现的问题。我很想知道我错了,这些都有简单的解决方案。

    需要外部的 try/catch 或最终的 Promise.catch(),否则会丢失错误和异常。

    最终的等待需要 Promise.then() 或额外的外部异步函数。

    只能使用 for/of 正确完成迭代,不能使用其他迭代器。

    Await 一次只能等待一个 Promise,而不是像 Promise 链中的 Promise.all 这样的并行 Promise。

    Await 不支持 Promise.race(),如果需要的话。

【讨论】:

1 对于 Promises 和 Async Await 都为真 你能详细说明3吗? 我将 #3 建立在我读到的一些我再也找不到的东西上。事实上,jakearchibald.com/2017/async-iterators-and-generators,从大约 3 年前的 Chrome 开始,表明异步与迭代器一起工作得很好。不知道这篇文章对Chrome等浏览器是否还有效,也没有时间尝试这种异步技术。

以上是关于使用异步/等待与承诺的区别?的主要内容,如果未能解决你的问题,请参考以下文章

在自定义承诺上使用异步等待

在自定义承诺上使用异步等待

异步等待所有承诺

异步函数 - 等待不等待承诺

Javascript - 异步等待和获取 - 返回值,而不是承诺?

在异步/等待中包装承诺