我在代码中的几个地方使用async
/ await
。
例如,如果我有这个功能:
async function func(x) {
...
return y;
}
然后我总是这样称呼它:
async function func2(x) {
let y = await func(x);
...
}
我注意到在某些情况下,我可以省略await
并且程序仍然可以正常运行,因此我无法弄清楚何时必须使用await
以及何时可以放弃它。
我的结论是,它是合法的"仅将await
直接放在return语句中。
例如:
async function func2(x) {
...
return func(x); // instead of return await func(x);
}
这个结论是否正确,否则,我在这里缺少什么?
修改
一个小的(但很重要的)概念,在我刚刚遇到并实现的任何答案中都没有提及:
它不是合法的"如果被调用的函数可能抛出异常,则将await
放在return
语句中,因此该语句在try
块内执行。
例如,删除以下代码中的await
是"危险":
async function func1() {
try {
return await func2();
}
catch (error) {
return something_else;
}
}
原因是try
块无异常地完成,Promise
对象返回"通常"。但是,在调用外部函数的任何函数中,当此Promise
对象执行""时,将发生实际错误并抛出异常。仅当使用await
时,才会在外部函数中成功处理此异常。否则,该责任就会增加,需要额外的try
/ catch
条款。
答案 0 :(得分:8)
如果func
是异步函数,那么使用和不使用await
调用它会产生不同的效果。
async function func(x) {
return x;
}
let y = await func(1); // 1
let z = func(1) // Promise (resolves to 1)
省略await
关键字总是合法的,但意味着你必须处理传统风格的承诺(首先要击败异步/等待点)。
func(1).then(z => /* use z here */)
如果你的return语句使用await
,那么你可以确定如果它抛出错误,它可以被捕获到你的函数中,而不是被调用它的代码。
答案 1 :(得分:2)
await
只是让您在async
函数中使用时将promises视为值。
另一方面,async
完全相反,它标记函数返回一个承诺,即使它恰好返回一个真实的同步值(听起来很奇怪)对于异步函数...但是当你有一个函数根据条件返回一个值或一个promise时经常发生。)
所以:
我已经得出结论,仅在返回声明中直接删除await是“合法的”。
在return
函数的最后一个async
语句中,您只是返回一个Promise,要么实际上是直接返回一个promise,一个实际值,还是一个Promise-as-value with await
关键字。
因此,在return语句中使用await
是非常多余的:您正在使用await
将promise转换为值 - 在异步执行的上下文中 - 但是{函数的{1}}标记会将该值视为承诺。
所以是的,将async
放在最后一个返回语句中总是安全的。
PS:实际上,await期望任何 thenable ,即具有await
属性的对象:它不需要完全符合规范的Promise,afaik。
PS2:当然,在调用同步函数时,您总是可以删除then
关键字:根本不需要它。
答案 2 :(得分:1)
所以请记住,这些异步函数的写法都是一样的:
// tedious, sometimes necessary
async function foo() {
return new Promise((resolve) => resolve(1)))
}
// shorter
async function foo() {
return Promise.resolve(1)
}
// very concise but calling `foo` still returns a promise
async function foo() {
return 1 // yes this is still a promise
}
您可以通过foo().then(console.log)
致电所有人,以打印1
。或者你可以通过await foo()
从另一个异步函数中调用它们,但并不总是需要立即等待这个承诺。
正如其他答案所指出的那样,await
解析了成功时对实际返回值语句的承诺(或者会在失败时抛出异常),而没有await
则只返回未决的承诺将来可能成功或失败的实例。
省略(即:小心使用它)await
的另一个用例是,在编写异步代码时,您可能最想要并行化任务。 await
可能会阻碍你。
在异步函数的范围内比较这两个示例:
async function func() {
const foo = await tediousLongProcess("foo") // wait until promise is resolved
const bar = await tediousLongProcess("bar") // wait until promise is resolved
return Promise.resolve([foo, bar]) // Now the Promise of `func` is marked as a success. Keep in mind that `Promise.resolve` is not necessary, `return [foo, bar]` suffices. And also keep in mind that an async function *always* returns a Promise.
}
使用:
async function func() {
promises = [tediousLongProcess("foo"), tediousLongProcess("bar")]
return Promise.all(promises) // returns a promise on success you have its values in order
}
第一个将比上一个花费更长的时间,因为顾名思义的每个await
将停止执行,直到您解决第一个承诺,然后是下一个承诺。
在第二个例子中,Promise.all
承诺将同时等待并解决任何顺序,一旦所有承诺都得到解决,结果将被订购。
(Bluebird promise库还提供了一个很好的Bluebird.map
函数,您可以在其中定义并发性,因为Promise.all
可能会削弱您的系统。)
我想在处理实际值时只使用await
。如果我只想要一个承诺,就没有必要等待它的价值,在某些情况下它实际上可能会损害你的代码的性能。
答案 3 :(得分:-1)
上面我得到了一个很好的答案,这只是我发现的另一种解释。
假设我有这个:
async function func(x) {
...
return y;
}
async function func2(x) {
...
return await func(x);
}
async function func3(x) {
let y = await func2(x);
...
}
我可以安全地删除await
上的return语句中的func2
的原因是,当我在{{1}中调用await
时,我已经有了func2
}}
基本上,在上面的func3
我有类似func3
的内容。
当然,这没有任何害处,因此保留await await func(x)
以确保所需的操作可能会更好。