-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: improve handling of promises in defaultTypeResolver #3494
base: main
Are you sure you want to change the base?
Conversation
This means that a mixture of sync/async calls becomes sync unnecessarily, as we return sync value…. Maybe catch handlers should be added to the promises so they reject silently? Is that possible? |
I had considered that, but silently swallowing errors seems like a bad idea. I think it makes sense for execution to become async if any async functions are called. This should be a pretty rare case, but if these errors were swallowed silently it could become very tricky to debug. |
Maybe there should be a side channel for logging these errors then so as they are not silent? In fact, this can be addressed currently in user space, as long as isTypeOf functions catch their errors and log them… this differs from field or type resolvers which must never reject, but it’s not quite a problem for an isTypeOf resolver fail if another succeeds. not 100% sure what the right answer is |
A side channel might be useful as well in context of #2974 to report the first error to client and potentially log all errors |
But as there currently is no side channel, I think I would approve these changes, and then refactor later |
The latest changes of this PR are available on NPM as Also you can depend on latest version built from this PR: |
✅ Deploy Preview for compassionate-pike-271cb3 ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
kinda forgot about this PR, but I think this change would still be good to get in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are 2 different questions:
- Should there ever be unhandled promise rejections?
- Must all errors be reported?
There probably should never be unhandled promise rejections, but we do not necessarily need to report all errors.
@@ -1618,6 +1618,10 @@ export const defaultTypeResolver: GraphQLTypeResolver<unknown, unknown> = | |||
if (isPromise(isTypeOfResult)) { | |||
promisedIsTypeOfResults[i] = isTypeOfResult; | |||
} else if (isTypeOfResult) { | |||
if (promisedIsTypeOfResults.length > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should probably silently ignore the errors....
Something like:
if (promisedIsTypeOfResults.length > 0) {
Promise.all(promisedIsTypeOfResults).then(undefined, () => {
/* ignore errors */
});
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seeing as one functioning isTypeOf resolver is all one should have and all one needs.
Compare also to a sync case with multiple sync isTypeOf resolvers, one that returns true, and multiple that throw instead of returning false. If the order is serendipitous, the non-erroring one will return true and mask all the other potential errors.
In #3706, we convert a sync error to an async error, but that's the not happy path of an error. Here, this change will convert every synchronous isTypeOf to a non-synchronous....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compare also to multiple async errors, when we return immediately with the first async error, without awaiting the others. I would argue that #3706 is a bit funny, in that if we have a sync error, there is no need for:
return promiseForObject(...).finally(() => throw originalError);
we should just have:
promiseForObject(....).then(undefined, () =>{ /*ignore*/ });
throw originalError;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense, updated
59424d7
to
d8237f7
Compare
Currently it is possible to have unhandled promise rejections that arise from a mix of sync and async isTypeOf checks.
This should fix that issue.