You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@annotator.apache.org by ra...@apache.org on 2021/04/18 21:49:24 UTC

[incubator-annotator] 02/02: Close source iterators on cartesian exit

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 <ra...@apache.org>
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');
+    }
+  });
 });