Skip to content

Commit

Permalink
fixes #2: close remaining passed iterators after being closed (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelficarra committed Jul 3, 2024
1 parent 3476a51 commit 7a0247b
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 3 deletions.
15 changes: 14 additions & 1 deletion spec.emu
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,20 @@ copyright: false
1. Else,
1. Let _completion_ be Completion(Yield(_innerValue_)).
1. If _completion_ is an abrupt completion, then
1. Return ? IteratorClose(_iteratorRecord_, _completion_).
1. Set _completion_ to Completion(IteratorClose(_iteratorRecord_, _completion_)).
1. Repeat, while _items_ is not empty,
1. Let _trailingIter_ be the first element of _items_.
1. Remove the first element from _items_.
1. If _trailingIter_ is an Object, then
1. Let _nextMethod_ be Completion(Get(_trailingIter_, "next")).
1. If _nextMethod_ is an abrupt completion, then
1. If _completion_ is not a throw completion, set _completion_ to _nextMethod_.
1. Else,
1. Set _nextMethod_ to ! _nextMethod_.
1. If IsCallable(_nextMethod_), then
1. Let _record_ be the Iterator Record { [[Iterator]]: _iter_, [[NextMethod]]: _nextMethod_, [[Done]]: true }.
1. Set _completion_ to Completion(IteratorClose(_iteratorRecord_, _completion_)).
1. Return ? _completion_.
1. Return CreateIteratorFromClosure(_closure_, *"Iterator Helper"*, %IteratorHelperPrototype%, « »).
</emu-alg>
</emu-clause>
27 changes: 25 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,31 @@ function concatImpl<A, B, C, D>(iteratorA: IteratorOrIterable<A>, iteratorB: Ite
function concatImpl<A, B, C, D, E>(iteratorA: IteratorOrIterable<A>, iteratorB: IteratorOrIterable<B>, iteratorC: IteratorOrIterable<C>, iteratorD: IteratorOrIterable<D>, iteratorE: IteratorOrIterable<E>): Generator<A | B | C | D | E>
function concatImpl(...iterators: Array<IteratorOrIterable<unknown>>): Generator<unknown>
function* concatImpl(...iterators: Array<unknown>): Generator<unknown> {
for (const iter of iterators) {
yield* liftIterator(getIteratorFlattenable(iter, 'iterate-strings'));
let i = 0;
try {
for (; i < iterators.length; ++i) {
let iter = iterators[i];
yield* liftIterator(getIteratorFlattenable(iter, 'iterate-strings'));
}
} finally {
let err = null, hasErr = false;;
for (++i; i < iterators.length; ++i) {
try {
let obj = iterators[i];
if (obj != null && (typeof obj === 'object' || typeof obj === 'function')) {
let iter = obj as Iterator<unknown>;
if (typeof iter.next === 'function') {
iter.return?.();
}
}
} catch (e) {
if (!hasErr) {
hasErr = true;
err = e;
}
}
}
if (hasErr) throw err;
}
}

Expand Down
87 changes: 87 additions & 0 deletions test/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,90 @@ test('concat bare iterators', async t => {
[0, 1, 2, 3, 4, 5],
);
});

test('closes later iterators', async t => {
await t.test('stopped between iterators', () => {
let returned = [];
let concatted = Iterator.concat(
[0, 1],
[2, 3],
{
next() {
return { done: false, value: 'a' };
},
return() {
returned.push('a');
return { done: true, value: undefined };
},
},
{
next() {
return { done: false, value: 'b' };
},
return() {
returned.push('b');
return { done: true, value: undefined };
},
},
{
next() {
return { done: false, value: 'c' };
},
return() {
returned.push('c');
return { done: true, value: undefined };
},
},
);
assert.equal(concatted.next().value, 0);
assert.equal(concatted.next().value, 1);
assert.equal(concatted.next().value, 2);
assert.equal(concatted.next().value, 3);
assert.deepEqual(returned, []);
concatted.return();
assert.deepEqual(returned, ['a', 'b', 'c']);
});

await t.test('stopped during iteration', () => {
let returned = [];
let concatted = Iterator.concat(
[0, 1],
[2, 3],
{
next() {
return { done: false, value: 4 };
},
return() {
returned.push('a');
return { done: true, value: undefined };
},
},
{
next() {
return { done: false, value: 'b' };
},
return() {
returned.push('b');
return { done: true, value: undefined };
},
},
{
next() {
return { done: false, value: 'c' };
},
return() {
returned.push('c');
return { done: true, value: undefined };
},
},
);
assert.equal(concatted.next().value, 0);
assert.equal(concatted.next().value, 1);
assert.equal(concatted.next().value, 2);
assert.equal(concatted.next().value, 3);
assert.equal(concatted.next().value, 4);
assert.deepEqual(returned, []);
concatted.return();
assert.deepEqual(returned, ['a', 'b', 'c']);
});
});

0 comments on commit 7a0247b

Please sign in to comment.