Skip to content
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

Merged
merged 2 commits into from
Aug 25, 2020

Conversation

robrichard
Copy link
Contributor

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

@@ -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,
Copy link
Contributor Author

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> {

let i = 0;
for (const friend of friends) {
// eslint-disable-next-line no-await-in-loop
await new Promise((r) => setTimeout(r, 1));
Copy link
Member

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.

Copy link
Contributor Author

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

i++;
}
// close iterator asynchronously
await new Promise((r) => setTimeout(r, 1));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

description:
'The friends of the droid, or an empty list if they have none. Returns an AsyncIterable',
args: {
errorIndex: { type: GraphQLInt },
Copy link
Member

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.

Copy link
Contributor Author

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

@IvanGoncharov IvanGoncharov added the PR: feature 🚀 requires increase of "minor" version number label Jul 15, 2020
* Complete a async iterable value by completing each item in the list with
* the inner type
*/

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra line

return iterator.next().then(
({ value, done }) => {
if (done) {
return;
Copy link
Member

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.

Copy link
Contributor Author

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.


const itemType = returnType.ofType;

const handleNext = () => {
Copy link
Member

@IvanGoncharov IvanGoncharov Jul 15, 2020

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.

Copy link
Contributor Author

@robrichard robrichard Jul 15, 2020

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.

const itemType = returnType.ofType;

const handleNext = () => {
const fieldPath = addPath(path, index);
Copy link
Member

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.

Copy link
Contributor Author

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:

const fieldPath = addPath(path, index, undefined);

@robrichard
Copy link
Contributor Author

@IvanGoncharov this is ready for another review whenever you have some time.

@IvanGoncharov
Copy link
Member

@robrichard Had a surgery so was offline for the last few weeks.
Now returning back to open-source so it will take me a few days to get back to this PR.

@robrichard
Copy link
Contributor Author

@IvanGoncharov no rush, please take your time

@IvanGoncharov
Copy link
Member

@robrichard I merged some changes to subscribe can you please rebase it?
It should pretty simple.

@robrichard
Copy link
Contributor Author

@IvanGoncharov this is up to date now

Comment on lines 912 to 913
const iteratorMethod = result[SYMBOL_ASYNC_ITERATOR];
const iterator = iteratorMethod.call(result);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@robrichard Can we do?

Suggested change
const iteratorMethod = result[SYMBOL_ASYNC_ITERATOR];
const iterator = iteratorMethod.call(result);
const iterator = result[SYMBOL_ASYNC_ITERATOR](result);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, updated.

@IvanGoncharov
Copy link
Member

@robrichard Thanks for your patience and sorry for such long delay.
I reviewed this PR in more detail and suspect we not handling a few cases like: completeValue throwing or returning a promise, etc.
Also, I feel like we need to figure out a way to unify more code between iterating async iterable and other types of collections.
In the next few days, I will try to add as much as possible missing test cases and we can proceed from there.

@IvanGoncharov IvanGoncharov added this to the 15.x.x milestone Aug 20, 2020
@IvanGoncharov IvanGoncharov changed the base branch from master to asynciteratable August 25, 2020 17:58
@IvanGoncharov IvanGoncharov merged commit f612eb6 into graphql:asynciteratable Aug 25, 2020
@IvanGoncharov
Copy link
Member

@robrichard I rebased this PR against master and added trivial commit on top of it.
To allow both of us to submit and review each other PRs I created branch asynciteratable and merge it there.

@robrichard robrichard deleted the asynciterable branch October 4, 2020 14:55
robrichard added a commit to robrichard/graphql-js that referenced this pull request Oct 4, 2020
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com>
robrichard added a commit to robrichard/graphql-js that referenced this pull request Oct 4, 2020
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com>
robrichard added a commit to robrichard/graphql-js that referenced this pull request Oct 9, 2020
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com>
robrichard added a commit to robrichard/graphql-js that referenced this pull request Oct 14, 2020
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
robrichard added a commit to lilianammmatos/graphql-js that referenced this pull request Oct 22, 2020
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
robrichard added a commit to lilianammmatos/graphql-js that referenced this pull request Oct 27, 2020
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
IvanGoncharov pushed a commit that referenced this pull request Oct 28, 2020
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
IvanGoncharov pushed a commit that referenced this pull request Oct 28, 2020
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
robrichard added a commit that referenced this pull request Nov 18, 2020
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
robrichard added a commit that referenced this pull request Dec 31, 2020
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
robrichard added a commit that referenced this pull request Feb 9, 2021
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
robrichard added a commit that referenced this pull request Feb 9, 2021
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
robrichard added a commit that referenced this pull request Feb 12, 2021
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
robrichard added a commit that referenced this pull request Feb 16, 2021
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR: feature 🚀 requires increase of "minor" version number
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants