await 仅在异步函数中有效 - 使用 mongoosejs exec() 时

Posted

技术标签:

【中文标题】await 仅在异步函数中有效 - 使用 mongoosejs exec() 时【英文标题】:await is only valid in async function - when using 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 的人……还是我?这就是我寻求帮助的原因......而反对票只是你的傲慢! 【参考方案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 时处理错误,您有两种选择:

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

    如果你不使用 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值得一试! 我投了赞成票。谢谢您的帮助。不知道为什么有人会否决你的答案。

以上是关于await 仅在异步函数中有效 - 使用 mongoosejs exec() 时的主要内容,如果未能解决你的问题,请参考以下文章

异步/等待仅在读取文件时才是有效的异步函数[重复]

await 仅在 async 函数中有效

SyntaxError:“await”仅在“async”函数中有效 - 需要帮助

await 仅对异步函数有效城市字典 api discord js

Promise.all()函数中的JS“等待仅在异步函数中有效”[重复]

全局调用异步函数时出错:“等待仅在异步函数和模块的顶层主体中有效”?