-
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
Execute: simplify + speedup #1286
Conversation
@@ -904,13 +871,6 @@ function completeValue( | |||
path: ResponsePath, | |||
result: mixed, | |||
): MaybePromise<mixed> { | |||
// If result is a Promise, apply-lift over completeValue. |
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.
Do we have a test that shows completing Array<Promise>
works?
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.
@leebyron Yes, it checked here:
graphql-js/src/execution/__tests__/lists-test.js
Lines 167 to 173 in 43ff3e3
describe('Array<Promise<T>>', () => { | |
it( | |
'Contains values', | |
check(type, [resolved(1), resolved(2)], { | |
data: { nest: { test: [1, 2] } }, | |
}), | |
); |
src/execution/execute.js
Outdated
return null; | ||
} catch (rawError) { | ||
const error = locatedFieldError(rawError, fieldNodes, path); | ||
return handleFieldError(error, returnType, exeContext); |
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.
locatedFieldError
and handleFieldError
are always called together - should they become a single function?
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.
@leebyron Yes, you're right. I merged them together 👍
result, | ||
); | ||
let completed; | ||
if (isPromise(result)) { |
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'm concerned moving this out of completeValue
may break some scenarios we might not have sufficient test coverage over.
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.
@leebyron completeValue
only called in 3 places:
graphql-js/src/execution/execute.js
Lines 907 to 912 in 43ff3e3
// If result is a Promise, apply-lift over completeValue. | |
if (isPromise(result)) { | |
return result.then(resolved => | |
completeValue(exeContext, returnType, fieldNodes, info, path, resolved), | |
); | |
} |
I moved this code to completeValueCatchingError
so nothing changed here.
graphql-js/src/execution/execute.js
Lines 919 to 929 in 43ff3e3
// If field type is NonNull, complete for inner type, and throw field error | |
// if result is null. | |
if (isNonNullType(returnType)) { | |
const completed = completeValue( | |
exeContext, | |
returnType.ofType, | |
fieldNodes, | |
info, | |
path, | |
result, | |
); |
This code is called after promise uplift so nothing changed here.
graphql-js/src/execution/execute.js
Line 849 in 43ff3e3
const completed = completeValue( |
Also nothing is broken here since this call moved to completeValueCatchingError
and completeValueCatchingError
now handles promise uplift by itself.
535813c
to
c4fe200
Compare
@leebyron I address review comments. Can you please take a second look? |
Really nice work simplifying this! |
* Simplify non-null tests * Remove 'check' function and write test directly * containSubset => deep.equal * Change order of errors because of #1286 * remove excessive async/await
For a feature, I'm working on I needed to understand how
execute
is working so I read through its implementation. I notice thatcompleteValueWithLocatedError
is called only fromcompleteValueCatchingError
and would be simpler to just merge them together.So main reason of this PR is to remove one layout of try/catch & isPromise/then and make error handling more obvious.
To check that I didn't introduce any performance regressions I profiled modified version of introspectionFromSchema-benchmark.js
During this profiling I discovered that hottest function is a fat arrow callback inside
executeFields
function:So I replaced
Array.reduce
withfor
cycle onObject.keys
and together with removal ofcompleteValueWithLocatedError
it resulted in ~15% speedup(9005 ticks vs 7612 ticks), see profile:Performance improvement achieved on a synthetic test and probably wouldn't account for any measurable speedup in most servers. Another small bonus is call stack reduction 1 call (2 calls if a value returned as a promise) for every field or item inside an array.
But most importantly this PR make it easier for a developer to trace how values are resolved/complete inside
execute
.