This is an automated email from the ASF dual-hosted git repository. randall pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-annotator.git
commit 5ba4b0ad8cdf9ce1338bebc6f2b24ffe588b6f3e Author: Randall Leeds <[email protected]> AuthorDate: Sun Apr 18 14:48:29 2021 -0700 Close source iterators on cartesian exit --- packages/dom/src/range/cartesian.ts | 71 +++++++++++++++++-------------- packages/dom/test/range/cartesian.test.ts | 30 +++++++++++++ 2 files changed, 68 insertions(+), 33 deletions(-) diff --git a/packages/dom/src/range/cartesian.ts b/packages/dom/src/range/cartesian.ts index 060a27b..00f88dc 100644 --- a/packages/dom/src/range/cartesian.ts +++ b/packages/dom/src/range/cartesian.ts @@ -38,46 +38,51 @@ export async function* cartesian<T>( return generator(); }); - // Track the number of non-exhausted iterators. - let active = iterators.length; + try { + // Track the number of non-exhausted iterators. + let active = iterators.length; - // Track all the values of each iterator in a log. - const logs = iterators.map(() => []) as T[][]; + // Track all the values of each iterator in a log. + const logs = iterators.map(() => []) as T[][]; - // Track the promise of the next value of each iterator. - const nexts = iterators.map((it) => it.next()); + // Track the promise of the next value of each iterator. + const nexts = iterators.map((it) => it.next()); - // Iterate the values of all the iterators in parallel and yield tuples from - // the partial product of each new value and the existing logs of the other - // iterators. - while (active) { - // Wait for the next result. - const result = await Promise.race(nexts); - const { index } = result.value; + // Iterate the values of all the iterators in parallel and yield tuples from + // the partial product of each new value and the existing logs of the other + // iterators. + while (active) { + // Wait for the next result. + const result = await Promise.race(nexts); + const { index } = result.value; - // If the iterator has exhausted all the values, set the promise - // of its next value to never resolve. - if (result.done) { - active--; - nexts[index] = new Promise(() => undefined); - continue; - } + // If the iterator has exhausted all the values, set the promise + // of its next value to never resolve. + if (result.done) { + active--; + nexts[index] = new Promise(() => undefined); + continue; + } - // Append the new value to the log. - const { value } = result.value; - logs[index].push(value); + // Append the new value to the log. + const { value } = result.value; + logs[index].push(value); - // Record the promise of the next value. - nexts[index] = iterators[index].next(); + // Record the promise of the next value. + nexts[index] = iterators[index].next(); - // Create a scratch input for computing a partial product. - const scratch = [...logs]; - scratch[index] = [value]; + // Create a scratch input for computing a partial product. + const scratch = [...logs]; + scratch[index] = [value]; - // Synchronously compute and yield tuples of the partial product. - yield* scratch.reduce( - (acc, next) => acc.flatMap((v) => next.map((w) => [...v, w])), - [[]] as T[][], - ); + // Synchronously compute and yield tuples of the partial product. + yield* scratch.reduce( + (acc, next) => acc.flatMap((v) => next.map((w) => [...v, w])), + [[]] as T[][], + ); + } + } finally { + const closeAll = iterators.map((it, index) => it.return({ index })); + await Promise.all(closeAll); } } diff --git a/packages/dom/test/range/cartesian.test.ts b/packages/dom/test/range/cartesian.test.ts index 475bc79..ff0ff8a 100644 --- a/packages/dom/test/range/cartesian.test.ts +++ b/packages/dom/test/range/cartesian.test.ts @@ -56,4 +56,34 @@ describe('cartesian', () => { assert.sameDeepMembers(actual, expected, 'yields the expected items'); }); + + it.only('re-raises exceptions and closes iterators', async () => { + let didClose = false; + const error = new Error(); + + + async function* throws() { + yield 1; + throw error; + } + + async function* works() { + try { + yield 2; + yield 3; + } finally { + didClose = true; + } + } + + try { + // eslint-disable-next-line + const cart = cartesian(throws(), works()); + await cart.next(); + await cart.next(); + } catch (e) { + assert.strictEqual(error, e, 're-raises an error from an iterable'); + assert.isTrue(didClose, 'closes the iterators'); + } + }); });
