如何正确处理异步错误? [英] How to handle async errors correctly?

查看:224
本文介绍了如何正确处理异步错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

进行GraphQL查询时,查询失败,Apollo通过具有数据对象和错误对象来解决此问题.

When making a GraphQL query, and the query fails, Apollo solves this by having a data-object and an error-object.

发生异步错误时,我们获得了具有一个数据对象和一个错误对象的相同功能.但是,这次我们也得到一个UnhandledPromiseRejectionWarning,其中包含有关以下内容的信息:DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code..

When an async error is happening, we get the same functionality with one data-object and one error-object. But, this time we get an UnhandledPromiseRejectionWarning too, with information about: DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code..

因此,我们显然需要解决此问题,但是我们希望异步函数将错误一直传递到Apollo.我们是否需要尝试...捕获所有功能,然后将错误进一步传递到树上?来自C#的一个例外是,如果从未捕获到一个异常,它会一直上升到顶端.要告诉Apollo GraphQL一个(或多个)叶子无法从数据库中检索数据,这听起来像是一件繁琐的工作.

So, we obviously need to solve this, but we want our async-functions to cast errors all the way up to Apollo. Do we need to try...catch all functions and just pass our error further up the tree? Coming from C#, were an exception just goes all the way to the top if never caught, it sounds like a tedious job to tell Apollo GraphQL that one (or more) leaves failed to retrieve data from the database.

是否有更好的方法来解决此问题,或者有什么办法告诉javascript/node一个未捕获的错误应进一步传递到调用树,直到被捕获?

Is there a better way to solve this, or is there any way to tell javascript/node that an uncaught error should be passed further up the call tree, until it's caught?

推荐答案

如果正确链接了诺言,您将永远不会看到此警告,并且GraphQL会捕获所有错误.假设我们有这两个返回Promise的函数,后者总是拒绝:

If you correctly chain your promises, you should never see this warning and all of your errors will be caught by GraphQL. Assume we have these two functions that return a Promise, the latter of which always rejects:

async function doSomething() {
  return
}

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

首先,给出一些正确的示例:

First, some correct examples:

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

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

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

In all of these cases, you'll see the error inside the errors array and no warning in your console. We could reverse the order of the functions (calling doSomething first) and this would still be the case.

现在,让我们破坏代码:

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

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

在这些示例中,我们关闭了该函数,但我们没有等待返回的Promise.这意味着我们的解析器将继续执行.如果未解决的Promise解决了,我们将无法处理其结果-如果它拒绝了,我们将无法对错误进行处理(警告未表明,该错误未得到处理).

In these examples, we're firing off the function, but we're not awaiting the returned Promise. That means execution of our resolver continues. If the unawaited Promise resolves, there's nothing we can do with its result -- if it rejects, there's nothing we can do about the error (it's unhandled, as the warning indicates).

通常,您应始终确保正确地链接了Promises(承诺),如上所示.使用async/await语法非常容易,因为错过return如果没有它,异常容易.

In general, you should always ensure your Promises are chained correctly as shown above. This is significantly easier to do with async/await syntax, since it's exceptionally easy to miss a return without it.

副作用如何?

可能有一些函数返回您要运行的Promise,但又不想暂停您的解析程序的执行. Promise解决还是返回与您的解决程序返回的内容无关,您只需要运行它即可.在这种情况下,我们只需要catch来处理被拒绝的承诺:

There may be functions that return a Promise that you want to run, but don't want to pause your resolver's execution for. Whether the Promise resolves or returns is irrelevant to what your resolver returns, you just need it to run. In these cases, we just need a catch to handle the promise being rejected:

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

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

Here, we call alwaysReject and execution continues onto doSomething. If alwaysReject eventually rejects, the error will be caught and no warning will be shown in the console.

注意:这些副作用"没有被等待,这意味着GraphQL执行将继续,并且在它们仍在运行时很可能会完成.没办法在GraphQL响应(即errors数组)中包括副作用带来的错误,充其量您只能将它们记录下来.如果您希望某个Promise的拒绝原因出现在响应中,则需要在解析器中等待它,而不是将其视为副作用.

Note: These "side effects" are not awaited, meaning GraphQL execution will continue and could very well finish while they are still running. There's no way to include errors from side effects inside your GraphQL response (i.e. the errors array), at best you can just log them. If you want a particular Promise's rejection reason to show up in the response, you need to await it inside your resolver instead of treating it like a side effect.

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

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

When dealing with Promises, we often see errors caught after our function call, for example:

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

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

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

This is important inside a synchronous context (for example, when building a REST api with express). Failing to catch rejected promises will result in the familiar UnhandledPromiseRejectionWarning. However, because GraphQL's execution layer effectively functions as one giant try/catch, it's not really necessary to catch your errors as long as your Promises are chained/awaited properly. This is true unless A) you're dealing with side effects as already illustrated, or B) you want to prevent the error from bubbling up:

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()

这篇关于如何正确处理异步错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆