【问题标题】:await is only valid in async function - when using mongoosejs exec()await 仅在异步函数中有效 - 使用 mongoosejs exec() 时
【发布时间】:2018-11-04 04:42:40
【问题描述】:

我正在将一个 Hapi 项目移植到 v17 并在迁移到 async/await 时遇到了 Mongoose 的一些问题。

对于我在模型(猫鼬)上使用“等待”的任何代码,例如对象:

const result = await User.findOne({email: email}).exec();

运行“node server.js”时出现以下异常

            await User.findOne({}).exec();
        ^^^^^

SyntaxError: await is only valid in async function
at new Script (vm.js:74:7)
at createScript (vm.js:246:10)
at Object.runInThisContext (vm.js:298:10)
at Module._compile (internal/modules/cjs/loader.js:670:28)
at Object.Module._extensions..js 
(internal/modules/cjs/loader.js:713:10)
at Module.load (internal/modules/cjs/loader.js:612:32)
at tryModuleLoad (internal/modules/cjs/loader.js:551:12)
at Function.Module._load (internal/modules/cjs/loader.js:543:3)
at Module.require (internal/modules/cjs/loader.js:650:17)
at require (internal/modules/cjs/helpers.js:20:18)

我正在运行 node v10.2.0 和 mongoose 5.1.2,但无法理解为什么会出现错误。

mongoosejs 文档明确指出,如 here 所述,在使用 async/await 时应使用 exec() 来返回承诺。

有什么建议吗?

【问题讨论】:

  • 在否决投票之前,您是否看过 mongoosejs 文档?因为他们声明“如果您使用的是 async/await,请等待 MyModel.findOne({}).exec()。”我认为它已经过测试并且是一个异步函数,否则为什么他们会在他们的文档中包含它。另外,我认为我不是世界上唯一一个同时使用 hapijs 和 mogoose 的人……还是我?这就是我寻求帮助的原因......而反对票只是你的傲慢!

标签: node.js mongoose hapijs


【解决方案1】:

await 只能在使用 async 关键字声明的函数内部使用。

async function doSomething() {
    let result = await someMongoooseFunctionThatReturnsAPromise();
    // use result here
}

await 不能在async 函数之外使用。这就是你的错误告诉你的,它与猫鼬完全无关。它与调用 mongoose 函数的代码的结构有关。

注意: 任何 node.js 事件驱动代码都已经在函数中,因此要在该函数中使用 await,您所要做的就是将 async 关键字添加到包含函数定义。如果该函数的调用者不期望任何返回结果,则不需要进一步更改。如果该函数的调用者期望返回结果,那么您必须调整调用代码以期望从 async 声明的函数返回一个承诺。


同样值得理解的是,async 函数总是返回一个承诺。虽然您可以像常规顺序代码一样编写它:

async function doSomething() {
    let result = await someMongoooseFunctionThatReturnsAPromise();
    // use result here
    result++;
    return result;
}

这个函数实际上是返回一个promise,这个promise的解析值将是函数的返回值。所以,当你像这样使用async 函数时,你必须使用返回的promise:

 doSomething().then(finalResult => {
     console.log(finalResult);
 });

因此,在您的特定代码中,如果您要使用 await,它需要在 async 函数内:

async function someFunc() {
    const result = await User.findOne({email: email}).exec();
    // now this will work and you can use result here
}

或者,您可以改用.then()

User.findOne({email: email}).exec().then(result => {
    // process result here
    // continue with other code that uses result here
});

注意:要在使用 async/await 时处理错误,您有两种选择:

  1. 您可以在任何async 声明的函数中使用传统的try/catchtry/catch 将捕获来自await 的任何被拒绝的承诺。

  2. 如果你不使用 try/catch 并且你的函数中的 await 被拒绝,那么函数本身返回的承诺将被拒绝,你的函数的调用者将获得被拒绝的承诺。

所以,这取决于情况。如果您想在本地处理拒绝,那么您必须在 await 周围使用 try/catch(就像您使用 .catch() 一样。如果您希望拒绝冒泡给调用者,以便他们看到被拒绝的承诺,那么您不需要try/catch,因为Javascript 解释器会通过拒绝async 函数返回的承诺来自动冒泡被拒绝的await

【讨论】:

  • 我赞成,你的解释很清楚。感谢您的帮助。
  • @MarcosCasagrande - 是的,无声的投票在这里是生活中的一个事实,但似乎一个简短的理由至少可以向作者提供他们做错了什么的反馈,并且充其量允许作者“修复”或改进他们的答案 - 从而使整个社区受益。
  • 我知道,在投票时强制使用 cmets 会更好。
  • @MarcosCasagrande - 我不会走那么远(有时已经在评论中表达了这种情绪),但如果评论了一些合理的“为什么”,它将为社区的利益做出更多贡献.
  • @MarcosCasagrande:这已经在元网站上讨论过、争论过、辩论过很多次了。例如:links。社区的决定一直是不这样做,因为它可能会被滥用,而且我们大多数人发现,在评论和否决时,我们通常不会得到问题或答案的改进,而是会得到争论。
【解决方案2】:

您需要将您的代码包含在这样的异步函数中

async function fetchFun() {
  const result = await User.findOne({email: email}).exec();

  console.log('Results fetched!'); //etc
}

fetchFun(); // <--

注意:您仍然必须在没有await 的情况下调用此函数,正如我用箭头指出的那样,因为您的代码必须有一些入口异步/等待代码(例如 C 中的 main() ) 并且那个入口函数不能用await调用

【讨论】:

  • 感谢您的积极反馈,我想过这样做,但这意味着我必须为每个 mongoose 调用添加额外的间接级别。我使用 Promise 的原始代码更加简洁优雅。
  • 在我看来,这只会给你的代码增加一个层次,并且会消除一些混乱的.then()链接
  • 当跳转到async/await世界时,你还必须使用try/catch来处理你通常可以用.then(...).catch()等处理的异常。但我认为async/await值得一试!
  • 我投了赞成票。谢谢您的帮助。不知道为什么有人会否决你的答案。
猜你喜欢
  • 1970-01-01
  • 2020-09-07
  • 2019-09-12
  • 1970-01-01
  • 2022-06-18
  • 2020-12-23
  • 2020-08-13
  • 2020-04-15
  • 2019-06-14
相关资源
最近更新 更多