我们应该在 Javascript 中选择 async await 而不是 Promise

should we choose async await over Promise in Javascript

我知道async await是镇上的新Promise,它是一种编写异步代码的新方法,我也知道

我们不必编写 .then,创建匿名函数来处理响应

Async/await 终于可以用相同的构造处理同步和异步错误,好老 try/catch

promise 链返回的错误堆栈没有给出错误发生位置的线索。但是,来自 async/await 的错误堆栈指向包含错误

的函数

等等...

但是 这里我做了一个简单的基准测试https://repl.it/repls/FormalAbandonedChimpanzee

在基准测试中,我有 运行 2 个循环 100 万次。 在第一个循环中,我调用了一个返回 1 的函数 在另一个函数中,我正在调用一个将 1 作为异常抛出的函数。

调用返回 1 的函数的第一个循环所花费的时间几乎是将 1 作为错误抛出的函数的一半。

这表明 throw 所用时间几乎是 return

所用时间的两倍
node v7.4 linux/amd64

return takes 1.233seconds
1000000
throw takes 2.128seconds
1000000

基准代码如下

function f1() {
  return 1;
}

function f2() {
  throw 1;
}

function parseHrtimeToSeconds(hrtime) {
    var seconds = (hrtime[0] + (hrtime[1] / 1e9)).toFixed(3);
    return seconds;
}

var sum = 0;
var start = 0;
var i = 0;

start = process.hrtime();
for (i = 0; i < 1e6; i++) {
  try {
    sum += f1();
  } catch (e) {
    sum += e;
  }
}
var seconds = parseHrtimeToSeconds(process.hrtime(start));
console.log('return takes ' + seconds + 'seconds');
console.log(sum);




sum = 0;
start = process.hrtime();
for (i = 0; i < 1e6; i++) {
  try {
    sum += f2();
  } catch (e) {
    sum += e;
  }
}

seconds = parseHrtimeToSeconds(process.hrtime(start));
console.log('throw takes ' + seconds + 'seconds');
console.log(sum);

您的基准与 async/await 与原始承诺之间的性能无关。我所能看到的是抛出错误需要更长的时间来计算。这是预期的。

回到主要问题,应该使用 async/await 而不是带有原始承诺的 .then 吗?

请记住,async/await 只是原始承诺的语法糖,因此对整体性能应该不会有太大影响。但是,它确实使您的代码更加线性,从而消除了开发人员的大量认知开销。

结论是用你喜欢的。 Promises 可以是 polyfill,但新语法不能,因此在决定使用哪种样式时您可能需要牢记这一点。


一些误解:

The error stack returned from a promise chain gives no clue of where the error happened

事实并非如此。快速检查:

function error() {
    return new Promise(function(res, rej) {
        res(undefined()); // uh oh
    });
}

error().then(console.log, e => console.log("Uh oh!", e.stack));

显示包括位置在内的整个错误堆栈。

大多数情况下,答案是 "it depends"。

在谈论性能之前,更重要的方面是代码的可维护性,以及 async/await 与原始 Promise.

的局限性

async/await 是顺序执行异步代码的好方法,而 Promise 使您能够并发 运行 异步代码。

async function foo() {
  const a = await backend.doSomething()
  const b = await backend.doAnotherThing()
  return a + b
}

在上面的代码中,backend.doAnotherThing() 直到 backend.doSomething() 返回后才会执行。另一方面:

function foo() {
  Promise.all([backend.doSomething(), backend.doAnotherThing()])
    .then(([a, b]) => {
       return a + b
    })
}

将执行这两个调用,并等待它们完成。

正如您提到的 async/await 的好处,我个人经常使用它。以上情况除外。

如果您需要性能并且对您来说,async/awaitPromise 之间的性能差异比 async/[= 的可读性优势更重要14=] 超过 Promise,无论如何继续。

只要是有意识的选择,应该没问题。

更新:正如德里克·森会功夫所提到的

您可以通过以下方式与 async/await 并行执行:

async function foo() {
  const p1 = backend.doSomething()
  const p2 = backend.doAnotherThing()
  return await p1 + await p2
}

基于

您可以使用 async/await

实现与 Promise.all 相同的行为
function foo() {
  Promise.all([backend.doSomething(), backend.doAnotherThing()])
    .then(([a, b]) => {
       return a + b
    })
}

async function foo() {
  const a = backend.doSomething()
  const b = backend.doAnotherThing()
  return await a + await b
}

后端任务同时发生,我们在 return 之前等待两者都完成。另见 MDN example I wrote

基于此,我不确定直接使用 Promises 是否比 async/await 有任何性能优势。