-
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
Support returning async iterables from resolver functions #2712
Support returning async iterables from resolver functions #2712
Conversation
57ca245
to
08acc44
Compare
08acc44
to
39a4c55
Compare
@@ -160,7 +159,7 @@ function subscribeImpl( | |||
// Note: Flow can't refine isAsyncIterable, so explicit casts are used. | |||
isAsyncIterable(resultOrStream) | |||
? mapAsyncIterator( | |||
((resultOrStream: any): AsyncIterable<mixed>), | |||
resultOrStream, |
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.
@IvanGoncharov since adding boolean %checks(value instanceof AsyncIterable)
to isAsyncIterable
I was able to remove the cast here, but I was not able to remove it from the other side of the ternary.
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/subscription/subscribe.js:166:9
Cannot call sourcePromise.then with function bound to onFulfill because mixed [1] is incompatible
with object type [2] in type argument Yield [3] of the return value of property @@asyncIterator of
the return value. [incompatible-call]
src/subscription/subscribe.js
[2] 117│ ): Promise<AsyncIterator<ExecutionResult> | ExecutionResult> {
:
163│ mapSourceToResponse,
164│ reportGraphQLError,
165│ )
166│ : resultOrStream,
167│ );
168│ }
169│
:
[1] 206│ ): Promise<AsyncIterable<mixed> | ExecutionResult> {
/private/tmp/flow/flowlib_5bfb8b1/core.js
[3] 562│ interface $AsyncIterator<+Yield,+Return,-Next> {
src/__tests__/starWarsSchema.js
Outdated
let i = 0; | ||
for (const friend of friends) { | ||
// eslint-disable-next-line no-await-in-loop | ||
await new Promise((r) => setTimeout(r, 1)); |
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.
Why do you need setTimeout
?
A promise is always processed on a next tick even if it already resolved.
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.
Leftover from when I was trying to time results for tests combining @stream
and @defer
. Not needed any more and removed from this PR
src/__tests__/starWarsSchema.js
Outdated
i++; | ||
} | ||
// close iterator asynchronously | ||
await new Promise((r) => setTimeout(r, 1)); |
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.
same here.
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.
removed
src/__tests__/starWarsSchema.js
Outdated
description: | ||
'The friends of the droid, or an empty list if they have none. Returns an AsyncIterable', | ||
args: { | ||
errorIndex: { type: GraphQLInt }, |
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.
It's better to move non-trivial tests here: https://github.com/graphql/graphql-js/blob/master/src/execution/__tests__/lists-test.js
Starwars schema is intended as just a sample schema to quickly demonstrate all basic features.
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.
Removed these tests and added new tests to lists-test.js
src/execution/execute.js
Outdated
* Complete a async iterable value by completing each item in the list with | ||
* the inner type | ||
*/ | ||
|
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.
Extra line
src/execution/execute.js
Outdated
return iterator.next().then( | ||
({ value, done }) => { | ||
if (done) { | ||
return; |
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.
You can return completedResults
here right?
And save one tick.
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.
Yes, updated to return completedResults
here and in the error handler.
src/execution/execute.js
Outdated
|
||
const itemType = returnType.ofType; | ||
|
||
const handleNext = () => { |
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.
Usually, if we don't need this
we use standard function
syntax and put such functions after return of the main 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.
I refactored this a little bit so the handleNext
function is no longer required. We do not need to create inline functions which rely on closures any more.
src/execution/execute.js
Outdated
const itemType = returnType.ofType; | ||
|
||
const handleNext = () => { | ||
const fieldPath = addPath(path, index); |
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.
Can you please check if you need to add third argument.
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 not sure I understand what the third argument is for and when it should be passed. I updated it to pass undefined
as that's what's done currently for list values:
graphql-js/src/execution/execute.js
Line 944 in 6012d28
const fieldPath = addPath(path, index, undefined); |
bc704f9
to
56d2ba0
Compare
@IvanGoncharov this is ready for another review whenever you have some time. |
@robrichard Had a surgery so was offline for the last few weeks. |
@IvanGoncharov no rush, please take your time |
@robrichard I merged some changes to |
56d2ba0
to
1ff8a36
Compare
@IvanGoncharov this is up to date now |
src/execution/execute.js
Outdated
const iteratorMethod = result[SYMBOL_ASYNC_ITERATOR]; | ||
const iterator = iteratorMethod.call(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.
@robrichard Can we do?
const iteratorMethod = result[SYMBOL_ASYNC_ITERATOR]; | |
const iterator = iteratorMethod.call(result); | |
const iterator = result[SYMBOL_ASYNC_ITERATOR](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.
Yes, updated.
93bbece
to
0ef8163
Compare
0ef8163
to
65450d3
Compare
@robrichard Thanks for your patience and sorry for such long delay. |
65450d3
to
f0c3215
Compare
@robrichard I rebased this PR against |
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com>
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com>
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com>
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com> support async benchmark tests add benchmark tests for list fields add test for error from completeValue in AsyncIterable resolver change execute implementation for async iterable resolvers correctly handle promises returned by completeValue in async iterable resovlers
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com> support async benchmark tests add benchmark tests for list fields add test for error from completeValue in AsyncIterable resolver change execute implementation for async iterable resolvers correctly handle promises returned by completeValue in async iterable resovlers
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com> support async benchmark tests add benchmark tests for list fields add test for error from completeValue in AsyncIterable resolver change execute implementation for async iterable resolvers correctly handle promises returned by completeValue in async iterable resovlers
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com> support async benchmark tests add benchmark tests for list fields add test for error from completeValue in AsyncIterable resolver change execute implementation for async iterable resolvers correctly handle promises returned by completeValue in async iterable resovlers
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com> support async benchmark tests add benchmark tests for list fields add test for error from completeValue in AsyncIterable resolver change execute implementation for async iterable resolvers correctly handle promises returned by completeValue in async iterable resovlers
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com> support async benchmark tests add benchmark tests for list fields add test for error from completeValue in AsyncIterable resolver change execute implementation for async iterable resolvers correctly handle promises returned by completeValue in async iterable resovlers
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com> support async benchmark tests add benchmark tests for list fields add test for error from completeValue in AsyncIterable resolver change execute implementation for async iterable resolvers correctly handle promises returned by completeValue in async iterable resovlers
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com> support async benchmark tests add benchmark tests for list fields add test for error from completeValue in AsyncIterable resolver change execute implementation for async iterable resolvers correctly handle promises returned by completeValue in async iterable resovlers
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com> support async benchmark tests add benchmark tests for list fields add test for error from completeValue in AsyncIterable resolver change execute implementation for async iterable resolvers correctly handle promises returned by completeValue in async iterable resovlers
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com> support async benchmark tests add benchmark tests for list fields add test for error from completeValue in AsyncIterable resolver change execute implementation for async iterable resolvers correctly handle promises returned by completeValue in async iterable resovlers
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com> support async benchmark tests add benchmark tests for list fields add test for error from completeValue in AsyncIterable resolver change execute implementation for async iterable resolvers correctly handle promises returned by completeValue in async iterable resovlers
Returning AsyncIterables in resolver methods is useful when
@stream
is supported and can be added without any breaking changes. This is split out from #2319