javascript - 如何正确处理异步错误?

标签 javascript node.js graphql apollo-server

当进行 GraphQL 查询时,查询失败,Apollo 通过拥有一个数据对象和一个错误对象来解决这个问题。

当发生异步错误时,我们通过一个数据对象和一个错误对象获得相同的功能。但是,这次我们也收到了 UnhandledPromiseRejectionWarning,其中包含以下信息:DeprecationWarning:不推荐使用未处理的 promise 拒绝。将来,未处理的 promise 拒绝将以非零退出代码终止 Node.js 进程。

所以,我们显然需要解决这个问题,但我们希望我们的异步函数将错误一直传递给 Apollo。我们是否需要尝试...捕获所有函数并将我们的错误进一步传递到树上?来自 C#,如果异常一直到顶部,如果从未被捕获,告诉 Apollo GraphQL 一个(或多个)叶子未能从数据库检索数据听起来是一项乏味的工作。

有没有更好的方法来解决这个问题,或者有什么方法可以告诉 javascript/node 应该将 Uncaught Error 进一步向上传递到调用树中,直到它被捕获为止?

最佳答案

如果你正确地链接了你的 promise ,你应该永远不会看到这个警告,你的所有错误都会被 GraphQL 捕获。假设我们有这两个返回 Promise 的函数,后者总是拒绝:

async function doSomething() {
  return
}

async function alwaysReject() {
  return Promise.reject(new Error('Oh no!'))
}

首先,一些正确的例子:

someField: async () => {
  await alwaysReject()
  await doSomething()
},

// Or without async/await syntax
someField: () => {
  return alwaysReject()
    .then(() => {
      return doSomething()
    })
  // or...
  return alwaysReject().then(doSomething)
},

在所有这些情况下,您将在 errors 数组中看到错误,并且在您的控制台中没有警告。我们可以颠倒函数的顺序(先调用 doSomething),情况仍然如此。

现在,让我们来破解我们的代码:

someField: async () => {
  alwaysReject()
  await doSomething()
},

someField: () => {
  alwaysReject() // <-- Note the missing return
    .then(() => {
      return doSomething()
    })
},

在这些例子中,我们触发函数,但我们不等待返回的 Promise。这意味着我们的解析器会继续执行。如果未等待的 Promise 解决,我们无法对其结果做任何事情——如果它拒绝,我们就无法对错误做任何事情(它未处理,如警告所示)。

一般来说,您应该始终确保您的 Promise 已正确链接,如上所示。这使用 async/await 语法要容易得多,因为没有它很容易错过 return

副作用呢?

可能有些函数会返回您想要运行的 Promise,但不想暂停解析器的执行。 Promise 是解析还是返回与您的解析器返回什么无关,您只需要它运行即可。在这些情况下,我们只需要一个 catch 来处理被拒绝的 promise :

someField: async () => {
  alwaysReject()
    .catch((error) => {
      // Do something with the error
    })
  await doSomething()
},

在这里,我们调用 alwaysReject 并继续执行 doSomething。如果 alwaysReject 最终拒绝,错误将被捕获并且控制台中不会显示任何警告。

注意:这些“副作用”不会等待,这意味着 GraphQL 执行将继续,并且很可能在它们仍在运行时完成。无法在 GraphQL 响应(即 errors 数组)中包含来自副作用的错误,最多只能记录它们。如果您希望特定 Promise 的拒绝原因出现在响应中,您需要在解析器中等待它,而不是将其视为副作用。

关于 try/catch 和 catch 的最后一句话

在处理 Promises 时,我们经常会看到函数调用后出现错误,例如:

try {
  await doSomething()
} catch (error) {
  // handle error
}

return doSomething.catch((error) => {
  //handle error
})

这在同步上下文中很重要(例如,在使用 express 构建 REST api 时)。未能捕捉到被拒绝的 promise 将导致熟悉的 UnhandledPromiseRejectionWarning。但是,由于 GraphQL 的执行层有效地充当了一个巨大的 try/catch,因此只要您的 Promises 被正确地链接/等待,就没有必要捕获您的错误。这是真的,除非 A) 你正在处理已经说明的副作用,或者 B) 你想防止错误冒泡:

try {
  // execution halts because we await
  await alwaysReject()
catch (error) {
  // error is caught, so execution will continue (unless I throw the error)
  // because the resolver itself doesn't reject, the error won't be bubbled up
}
await doSomething()

关于javascript - 如何正确处理异步错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55025673/

相关文章:

javascript - 视频JS未安装flash提示

javascript - 在 Reactjs 中导入另一个(辅助)类

javascript - 拉斐尔:设置线条颜色

node.js - 如何将open api YAML结构转换为json

javascript - 环回扩展访问 token 模型会导致问题

orm - GraphQL, Dataloader, [ORM 与否], 有很多关系理解

javascript - 内联 CKEditor 在启动时将 <br> 添加到空 div

node.js - Azure Function Runtime 版本 : 2. 0.11888.0(测试版)变得无法连接到 cosmosDB

graphql - HATEOAS vs GraphQL 为微服务设置的决策标准?

java - 使用 graphql-java-tools 和 graphql-spring-boot-starter 进行错误处理