Skip to content

polish: mapAsyncIterator #3696

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

Merged
merged 2 commits into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { expect } from 'chai';
import { describe, it } from 'mocha';

import { mapAsyncIterator } from '../mapAsyncIterator';
import { expectPromise } from '../../__testUtils__/expectPromise';

import { mapAsyncIterable } from '../mapAsyncIterable';

/* eslint-disable @typescript-eslint/require-await */
describe('mapAsyncIterator', () => {
describe('mapAsyncIterable', () => {
it('maps over async generator', async () => {
async function* source() {
yield 1;
yield 2;
yield 3;
}

const doubles = mapAsyncIterator(source(), (x) => x + x);
const doubles = mapAsyncIterable(source(), (x) => x + x);

expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });
Expand Down Expand Up @@ -42,7 +44,7 @@ describe('mapAsyncIterator', () => {
},
};

const doubles = mapAsyncIterator(iterable, (x) => x + x);
const doubles = mapAsyncIterable(iterable, (x) => x + x);

expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });
Expand All @@ -60,7 +62,7 @@ describe('mapAsyncIterator', () => {
yield 3;
}

const doubles = mapAsyncIterator(source(), (x) => x + x);
const doubles = mapAsyncIterable(source(), (x) => x + x);

const result = [];
for await (const x of doubles) {
Expand All @@ -76,7 +78,7 @@ describe('mapAsyncIterator', () => {
yield 3;
}

const doubles = mapAsyncIterator(source(), (x) => Promise.resolve(x + x));
const doubles = mapAsyncIterable(source(), (x) => Promise.resolve(x + x));

expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });
Expand All @@ -100,7 +102,7 @@ describe('mapAsyncIterator', () => {
}
}

const doubles = mapAsyncIterator(source(), (x) => x + x);
const doubles = mapAsyncIterable(source(), (x) => x + x);

expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });
Expand Down Expand Up @@ -139,7 +141,7 @@ describe('mapAsyncIterator', () => {
},
};

const doubles = mapAsyncIterator(iterable, (x) => x + x);
const doubles = mapAsyncIterable(iterable, (x) => x + x);

expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });
Expand All @@ -164,7 +166,7 @@ describe('mapAsyncIterator', () => {
}
}

const doubles = mapAsyncIterator(source(), (x) => x + x);
const doubles = mapAsyncIterable(source(), (x) => x + x);

expect(await doubles.next()).to.deep.equal({ value: 'aa', done: false });
expect(await doubles.next()).to.deep.equal({ value: 'bb', done: false });
Expand Down Expand Up @@ -203,20 +205,15 @@ describe('mapAsyncIterator', () => {
},
};

const doubles = mapAsyncIterator(iterable, (x) => x + x);
const doubles = mapAsyncIterable(iterable, (x) => x + x);

expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });

// Throw error
let caughtError;
try {
/* c8 ignore next 2 */
await doubles.throw('ouch');
} catch (e) {
caughtError = e;
}
expect(caughtError).to.equal('ouch');
const message = 'allows throwing errors when mapping async iterable';
const thrown = doubles.throw(new Error(message));
await expectPromise(thrown).toRejectWith(message);
});

it('passes through caught errors through async generators', async () => {
Expand All @@ -231,7 +228,7 @@ describe('mapAsyncIterator', () => {
}
}

const doubles = mapAsyncIterator(source(), (x) => x + x);
const doubles = mapAsyncIterable(source(), (x) => x + x);

expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });
Expand All @@ -258,24 +255,14 @@ describe('mapAsyncIterator', () => {
throw new Error('Goodbye');
}

const doubles = mapAsyncIterator(source(), (x) => x + x);
const doubles = mapAsyncIterable(source(), (x) => x + x);

expect(await doubles.next()).to.deep.equal({
value: 'HelloHello',
done: false,
});

let caughtError;
try {
/* c8 ignore next 2 */
await doubles.next();
} catch (e) {
caughtError = e;
}

expect(caughtError)
.to.be.an.instanceOf(Error)
.with.property('message', 'Goodbye');
await expectPromise(doubles.next()).toRejectWith('Goodbye');
});

async function testClosesSourceWithMapper<T>(mapper: (value: number) => T) {
Expand All @@ -293,21 +280,11 @@ describe('mapAsyncIterator', () => {
}
}

const throwOver1 = mapAsyncIterator(source(), mapper);
const throwOver1 = mapAsyncIterable(source(), mapper);

expect(await throwOver1.next()).to.deep.equal({ value: 1, done: false });

let expectedError;
try {
/* c8 ignore next 2 */
await throwOver1.next();
} catch (error) {
expectedError = error;
}

expect(expectedError)
.to.be.an.instanceOf(Error)
.with.property('message', 'Cannot count to 2');
await expectPromise(throwOver1.next()).toRejectWith('Cannot count to 2');

expect(await throwOver1.next()).to.deep.equal({
value: undefined,
Expand Down
4 changes: 2 additions & 2 deletions src/execution/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import {
collectFields,
collectSubfields as _collectSubfields,
} from './collectFields';
import { mapAsyncIterator } from './mapAsyncIterator';
import { mapAsyncIterable } from './mapAsyncIterable';
import { getArgumentValues, getVariableValues } from './values';

/* eslint-disable max-params */
Expand Down Expand Up @@ -1143,7 +1143,7 @@ function mapSourceToResponse(
// the GraphQL specification. The `execute` function provides the
// "ExecuteSubscriptionEvent" algorithm, as it is nearly identical to the
// "ExecuteQuery" algorithm, for which `execute` is also used.
return mapAsyncIterator(resultOrStream, (payload: unknown) =>
return mapAsyncIterable(resultOrStream, (payload: unknown) =>
executeImpl(buildPerEventExecutionContext(exeContext, payload)),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { PromiseOrValue } from '../jsutils/PromiseOrValue';
* Given an AsyncIterable and a callback function, return an AsyncIterator
* which produces values mapped via calling the callback function.
*/
export function mapAsyncIterator<T, U, R = undefined>(
export function mapAsyncIterable<T, U, R = undefined>(
iterable: AsyncGenerator<T, R, void> | AsyncIterable<T>,
callback: (value: T) => PromiseOrValue<U>,
): AsyncGenerator<U, R, void> {
Expand Down