省略等待'是否合法?在某些情况下?

时间:2017-11-21 08:43:00

标签: javascript async-await es6-promise

我在代码中的几个地方使用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条款。

4 个答案:

答案 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)

async function始终返回Promise

所以请记住,这些异步函数的写法都是一样的:

// 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)以确保所需的操作可能会更好。