UNPKG

@graphql-tools/executor

Version:

Fork of GraphQL.js' execute function

97 lines (96 loc) 3.95 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.flattenAsyncIterable = flattenAsyncIterable; const disposablestack_1 = require("@whatwg-node/disposablestack"); /** * Given an AsyncIterable of AsyncIterables, flatten all yielded results into a * single AsyncIterable. */ function flattenAsyncIterable(iterable) { // You might think this whole function could be replaced with // // async function* flattenAsyncIterable(iterable) { // for await (const subIterator of iterable) { // yield* subIterator; // } // } // // but calling `.return()` on the iterator it returns won't interrupt the `for await`. const topIterator = iterable[Symbol.asyncIterator](); let currentNestedIterator; let waitForCurrentNestedIterator; let done = false; async function next() { if (done) { return { value: undefined, done: true }; } try { if (!currentNestedIterator) { // Somebody else is getting it already. if (waitForCurrentNestedIterator) { await waitForCurrentNestedIterator; return await next(); } // Nobody else is getting it. We should! let resolve; waitForCurrentNestedIterator = new Promise(r => { resolve = r; }); const topIteratorResult = await topIterator.next(); if (topIteratorResult.done) { // Given that done only ever transitions from false to true, // require-atomic-updates is being unnecessarily cautious. done = true; return await next(); } // eslint is making a reasonable point here, but we've explicitly protected // ourself from the race condition by ensuring that only the single call // that assigns to waitForCurrentNestedIterator is allowed to assign to // currentNestedIterator or waitForCurrentNestedIterator. currentNestedIterator = topIteratorResult.value[Symbol.asyncIterator](); waitForCurrentNestedIterator = undefined; resolve(); return await next(); } const rememberCurrentNestedIterator = currentNestedIterator; const nestedIteratorResult = await currentNestedIterator.next(); if (!nestedIteratorResult.done) { return nestedIteratorResult; } // The nested iterator is done. If it's still the current one, make it not // current. (If it's not the current one, somebody else has made us move on.) if (currentNestedIterator === rememberCurrentNestedIterator) { currentNestedIterator = undefined; } return await next(); } catch (err) { done = true; throw err; } } return { next, async return() { done = true; await Promise.all([currentNestedIterator?.return?.(), topIterator.return?.()]); return { value: undefined, done: true }; }, async throw(error) { done = true; await Promise.all([currentNestedIterator?.throw?.(error), topIterator.throw?.(error)]); /* c8 ignore next */ throw error; }, [Symbol.asyncIterator]() { return this; }, async [disposablestack_1.DisposableSymbols.asyncDispose]() { done = true; await Promise.all([ currentNestedIterator?.[disposablestack_1.DisposableSymbols.asyncDispose]?.(), topIterator?.[disposablestack_1.DisposableSymbols.asyncDispose]?.(), ]); }, }; }