diff --git a/spec.emu b/spec.emu index 554ab7b..d475853 100644 --- a/spec.emu +++ b/spec.emu @@ -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%, « »). diff --git a/src/index.ts b/src/index.ts index a033c65..88b7679 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,8 +40,31 @@ function concatImpl(iteratorA: IteratorOrIterable, iteratorB: Ite function concatImpl(iteratorA: IteratorOrIterable, iteratorB: IteratorOrIterable, iteratorC: IteratorOrIterable, iteratorD: IteratorOrIterable, iteratorE: IteratorOrIterable): Generator function concatImpl(...iterators: Array>): Generator function* concatImpl(...iterators: Array): Generator { - 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; + if (typeof iter.next === 'function') { + iter.return?.(); + } + } + } catch (e) { + if (!hasErr) { + hasErr = true; + err = e; + } + } + } + if (hasErr) throw err; } } diff --git a/test/index.mjs b/test/index.mjs index 0ab53f7..71917d7 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -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']); + }); +}); \ No newline at end of file