function shouldBe(actual, expected) { if (actual !== expected) throw new Error('bad value: ' + actual); } function shouldThrow(func, errorMessage) { var errorThrown = false; var error = null; try { func(); } catch (e) { errorThrown = true; error = e; } if (!errorThrown) throw new Error('not thrown'); if (String(error) !== errorMessage) throw new Error(`bad error: ${String(error)}`); } class CallSite { constructor() { this.count = 0; } call() { return this.count++; } } (function () { class Arrays { constructor() { this.first = [ 0, 1, 2, 3 ]; this.second = [ 4, 5, 6, 7 ]; } *[Symbol.iterator]() { yield * this.first; yield * this.second; } } var arrays = new Arrays; let i = 0; for (let value of arrays) shouldBe(i++, value); }()); (function () { // throw should be propagated. let c1 = new CallSite; class Iterator { next(value) { return { value, done: false }; } 'throw'(value) { shouldBe(value, 42); c1.call(); throw new Error("OK"); } [Symbol.iterator]() { return this; } } function *gen() { let iter = new Iterator(); yield * iter; } let g = gen(); shouldBe(g.next(0).value, undefined); shouldBe(g.next(1).value, 1); shouldBe(g.next(2).value, 2); shouldThrow(() => { g.throw(42); }, `Error: OK`); shouldThrow(() => { g.throw(44); }, `44`); shouldBe(c1.count, 1); }()); (function () { // No `throw` method. let c1 = new CallSite; class Iterator { next(value) { return { value, done: false }; } 'return'(value) { shouldBe(value, undefined); c1.call(); return { value, done: true }; } [Symbol.iterator]() { return this; } } function *gen() { let iter = new Iterator(); yield * iter; } let g = gen(); shouldBe(g.next(0).value, undefined); shouldBe(g.next(1).value, 1); shouldBe(g.next(2).value, 2); shouldThrow(() => { g.throw(42); }, `TypeError: Delegated generator does not have a 'throw' method.`); shouldThrow(() => { g.throw(44); }, `44`); shouldBe(c1.count, 1); }()); (function () { // No `throw` method, `return` returns an incorrect result. let c1 = new CallSite; class Iterator { next(value) { return { value, done: false }; } 'return'(value) { shouldBe(value, undefined); c1.call(); } [Symbol.iterator]() { return this; } } function *gen() { let iter = new Iterator(); yield * iter; } let g = gen(); shouldBe(g.next(0).value, undefined); shouldBe(g.next(1).value, 1); shouldBe(g.next(2).value, 2); shouldThrow(() => { g.throw(42); }, `TypeError: Iterator result interface is not an object.`); shouldThrow(() => { g.throw(44); }, `44`); shouldBe(c1.count, 1); }()); (function () { // No `throw` method, No `return` method. class Iterator { next(value) { return { value, done: false }; } [Symbol.iterator]() { return this; } } function *gen() { let iter = new Iterator(); yield * iter; } let g = gen(); shouldBe(g.next(0).value, undefined); shouldBe(g.next(1).value, 1); shouldBe(g.next(2).value, 2); shouldThrow(() => { g.throw(42); }, `TypeError: Delegated generator does not have a 'throw' method.`); shouldThrow(() => { g.throw(44); }, `44`); }()); (function () { // `throw` does not throw. Not returns a object. class Iterator { next(value) { return { value, done: false }; } 'throw'(value) { } [Symbol.iterator]() { return this; } } function *gen() { let iter = new Iterator(); yield * iter; } let g = gen(); shouldBe(g.next(0).value, undefined); shouldBe(g.next(1).value, 1); shouldBe(g.next(2).value, 2); shouldThrow(() => { g.throw(42); }, `TypeError: Iterator result interface is not an object.`); shouldThrow(() => { g.throw(44); }, `44`); }()); (function () { // `throw` does not throw. If returned iterator result is marked as done, it becomes `return`. class Iterator { next(value) { return { value, done: false }; } 'throw'(value) { return { value, done: true }; } [Symbol.iterator]() { return this; } } function *gen() { let iter = new Iterator(); let result = yield * iter; shouldBe(result, 42); yield 21; } let g = gen(); shouldBe(g.next(0).value, undefined); shouldBe(g.next(1).value, 1); shouldBe(g.next(2).value, 2); shouldBe(g.throw(42).value, 21); shouldBe(g.next().done, true); shouldThrow(() => { g.throw(44); }, `44`); }()); (function () { // `return` returns done: false. class Iterator { next(value) { return { value, done: false }; } 'return'(value) { return { value, done: false }; } [Symbol.iterator]() { return this; } } function *gen() { let iter = new Iterator(); let result = yield * iter; yield result; yield 42; } let g = gen(); shouldBe(g.next(0).value, undefined); shouldBe(g.next(1).value, 1); shouldBe(g.next(2).value, 2); shouldBe(g.return(42).value, 42); shouldBe(g.return(42).done, false); }()); (function () { function *gen() { let result = yield * [ 0, 1, 2 ]; yield result; } let g = gen(); shouldBe(g.next().value, 0); shouldBe(g.next().value, 1); shouldBe(g.next().value, 2); shouldBe(g.next().value, undefined); }());