Javascript `return await promise` 和 `return promise` 之间的区别
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38708550/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Difference between `return await promise` and `return promise`
提问by PitaJ
Given the code samples below, is there any difference in behavior, and, if so, what are those differences?
鉴于下面的代码示例,行为是否有任何差异,如果有,这些差异是什么?
return await promise
return await promise
async function delay1Second() {
return (await delay(1000));
}
return promise
return promise
async function delay1Second() {
return delay(1000);
}
As I understand it, the first would have error-handling within the async function, and errors would bubble out of the async function's Promise. However, the second would require one less tick. Is this correct?
据我了解,第一个将在 async 函数中进行错误处理,错误将从 async 函数的 Promise 中冒泡出来。但是,第二个需要少一个刻度。这样对吗?
This snippet is just a common function to return a Promise for reference.
这段代码只是一个返回 Promise 以供参考的常用函数。
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
回答by Denis Washington
Most of the time, there is no observable difference between return
and return await
. Both versions of delay1Second
have the exact same observable behavior (but depending on the implementation, the return await
version might use slightly more memory because an intermediate Promise
object might be created).
大多数情况下,return
和之间没有明显的区别return await
。的两个版本都delay1Second
具有完全相同的可观察行为(但根据实现,该return await
版本可能会使用更多的内存,因为Promise
可能会创建一个中间对象)。
However, as @PitaJ pointed out, there is one case where there is a difference: if the return
or return await
is nested in a try
-catch
block. Consider this example
但是,正如@PitaJ 指出的那样,有一种情况存在差异:如果return
orreturn await
嵌套在try
-catch
块中。考虑这个例子
async function rejectionWithReturnAwait () {
try {
return await Promise.reject(new Error())
} catch (e) {
return 'Saved!'
}
}
async function rejectionWithReturn () {
try {
return Promise.reject(new Error())
} catch (e) {
return 'Saved!'
}
}
In the first version, the async function awaits the rejected promise before returning its result, which causes the rejection to be turned into an exception and the catch
clause to be reached; the function will thus return a promise resolving to the string "Saved!".
在第一个版本中,async 函数在返回结果之前等待被拒绝的 promise,这导致拒绝变成异常并到达catch
子句;因此,该函数将返回解析为字符串“已保存!”的承诺。
The second version of the function, however, does return the rejected promise directly without awaiting it within the async function, which means that the catch
case is notcalled and the caller gets the rejection instead.
然而,该函数的第二个版本确实直接返回被拒绝的承诺,而无需在 async 函数中等待它,这意味着不调用catch
case而是调用者获得拒绝。
回答by chharvey
As other answers mentioned, there is likely a slight performance benefit when letting the promise bubble up by returning it directly — simply because you don't have to await the result first and then wrap it with another promise again. However, no one has talked about tail call optimizationyet.
正如其他答案所提到的,通过直接返回承诺让承诺冒泡可能会带来轻微的性能优势 - 仅仅是因为您不必先等待结果然后再次用另一个承诺包装它。然而,还没有人谈论过尾调用优化。
Tail call optimization, or “proper tail calls”, is a technique that the interpreter uses to optimize the call stack. Currently, not many runtimes support it yet— even though it's technically part of the ES6 Standard— but it's possible support might be added in the future, so you can prepare for that by writing good code in the present.
尾调用优化,或“适当的尾调用”,是解释器用来优化调用栈的一种技术。目前,还没有多少运行时支持它——尽管它在技术上是ES6 标准的一部分——但将来可能会添加支持,因此您可以通过现在编写好的代码来为此做好准备。
In a nutshell, TCO (or PTC) optimizes the call stack by notopening a new frame for a function that is directly returned by another function. Instead, it reuses the same frame.
简而言之,TCO(或 PTC)通过不为另一个函数直接返回的函数打开新框架来优化调用堆栈。相反,它重用相同的框架。
async function delay1Second() {
return delay(1000);
}
Since delay()
is directly returned by delay1Second()
, runtimes supporting PTC will first open a frame for delay1Second()
(the outer function), but then instead of opening anotherframe for delay()
(the inner function), it will just reuse the same frame that was opened for the outer function. This optimizes the stack because it can prevent a stack overflow(hehe) with very large recursive functions, e.g., fibonacci(5e+25)
. Essentially it becomes a loop, which is much faster.
由于delay()
由 直接返回delay1Second()
,因此支持 PTC 的运行时将首先为delay1Second()
(外部函数)打开一个框架,但随后不会为(内部函数)打开另一个框架delay()
,它只会重用为外部函数打开的同一个框架。这优化了堆栈,因为它可以防止具有非常大的递归函数的堆栈溢出(hehe),例如,fibonacci(5e+25)
。从本质上讲,它变成了一个循环,速度要快得多。
PTC is only enabled when the inner function is directlyreturned. It's not used when the result of the function is altered before it is returned, for example, if you had return (delay(1000) || null)
, or return await delay(1000)
.
PTC 只有在直接返回内部函数时才启用。当函数的结果在返回之前被改变时,它不会被使用,例如,如果你有return (delay(1000) || null)
, 或return await delay(1000)
。
But like I said, most runtimes and browsers don't support PTC yet, so it probably doesn't make a huge difference now, but it couldn't hurt to future-proof your code.
但就像我说的,大多数运行时和浏览器还不支持 PTC,所以现在它可能不会有太大的不同,但它不会对你的代码面向未来造成伤害。
Read more in this question: Node.js: Are there optimizations for tail calls in async functions?
在这个问题中阅读更多内容:Node.js:异步函数中的尾调用是否有优化?
回答by nrabinowitz
This is a hard question to answer, because it depends in practice on how your transpiler (probably babel
) actually renders async/await
. The things that are clear regardless:
这是一个很难回答的问题,因为它实际上取决于您的转译器(可能babel
)实际如何呈现async/await
. 无论如何都清楚的事情:
Both implementations should behave the same, though the first implementation mayhave one less
Promise
in the chain.Especially if you drop the unnecessary
await
, the second version would not require any extra code from the transpiler, while the first one does.
两个实现的行为应该相同,尽管第一个实现在链中可能少
Promise
了一个。特别是如果您删除不必要的
await
,第二个版本不需要来自转译器的任何额外代码,而第一个版本则需要。
So from a code performance and debugging perspective, the second version is preferable, though only very slightly so, while the first version has a slight legibility benefit, in that it clearly indicates that it returns a promise.
因此,从代码性能和调试的角度来看,第二个版本更可取,尽管只是非常轻微,而第一个版本具有轻微的易读性优势,因为它清楚地表明它返回了一个承诺。
回答by Carlos Terrazas
here i leave some code practical for you can undertand it the diferrence
在这里我留下了一些实用的代码,你可以理解它的不同之处
let x = async function () {
return new Promise((res, rej) => {
setTimeout(async function () {
console.log("finished 1");
return await new Promise((resolve, reject) => { // delete the return and you will see the difference
setTimeout(function () {
resolve("woo2");
console.log("finished 2");
}, 5000);
});
res("woo1");
}, 3000);
});
};
(async function () {
var counter = 0;
const a = setInterval(function () { // counter for every second, this is just to see the precision and understand the code
if (counter == 7) {
clearInterval(a);
}
console.log(counter);
counter = counter + 1;
}, 1000);
console.time("time1");
console.log("hello i starting first of all");
await x();
console.log("more code...");
console.timeEnd("time1");
})();
the function "x" just is a function async than it have other fucn if will delete the return it print "more code..."
函数“x”只是一个异步函数,而不是其他fucn,如果将删除返回它打印“更多代码...”
the variable x is just an asynchronous function that in turn has another asynchronous function, in the main of the code we invoke a wait to call the function of the variable x, when it completes it follows the sequence of the code, that would be normal for "async / await ", but inside the x function there is another asynchronous function, and this returns a promise or returns a" promise "it will stay inside the x function, forgetting the main code, that is, it will not print the" console.log ("more code .. "), on the other hand if we put" await "it will wait for every function that completes and finally follows the normal sequence of the main code.
变量x只是一个异步函数,反过来又具有另一个异步函数,在代码的主体中我们调用了一个wait来调用变量x的函数,当它完成时它遵循代码的顺序,这将是正常的对于“async/await”,但在x函数内部还有另一个异步函数,这会返回一个promise或返回一个“promise”,它会留在x函数中,忘记主代码,也就是说,它不会打印" console.log ("more code .."),另一方面,如果我们放置"await",它将等待每个函数完成并最终遵循主代码的正常顺序。
below the "console.log (" finished 1 "delete the" return ", you will see the behavior.
在“console.log(”完成1“删除”返回“下方,您将看到行为。