我们应该在 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
/await
与 Promise
之间的性能差异比 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
有任何性能优势。
我知道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
/await
与 Promise
之间的性能差异比 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
有任何性能优势。