Bergi (2015-03-25T19:19:49.000Z)
d at domenic.me (2015-04-14T22:10:18.650Z)
Axel Rauschmayer wrote: > Given that redundant calls to `return()` don’t make a difference (h/t Bergi) I'm sorry, that was not 100% accurate. I only referred to `.return(x)` returning {done:true, value:x} and `.throw(e)` being equivalent to `throw e;` when the generator was never started or is already completed (http://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresumeabrupt). In fact, there are generators that behave differently when being prematurely aborted and attempted to be closed multiple times. A contrived example: ```js function* unstoppableCounter(n) { try { while (true) yield n++; } finally { console.log("unclosable!"); yield* unstoppableCounter(n); } } var counter = unstoppableCounter(0); var i=5; for (var x of counter) { console.log(x); if (!--i) break; } i=4; for (var x of counter) { console.log(x); if (!--i) break; } ``` Every call of `counter.return()` here would actually log "unclosable!", increase the counter and yield {done:false, value:…} - in general, might have side effects. So we shouldn't do it arbitrarily often. > couldn’t the iteration protocol be simpler if iterators were always closed. “Manually implemented” iterators could be written without the check in line (A) I don't think that's how an explicit iterator would be written. Shouldn't it look more like ```js … next() { if (iterationIsDone()) { cleanUp(); return {done: true}; } else { return {value: nextValue(), done: false}; } } ``` Of course that assumes that we don't have a return value, and `next()` is no more called after it returned `done: true` once (otherwise we'd clean up multiple times). Maybe better: ```js … next() { if (iterationIsDone()) { return {done: true}; } else { let result = {value: nextValue(), done: iterationIsDone()}; if (result.done) cleanUp(); // (B) return result; } } ``` Admittedly, that has the line you argued against again…
d at domenic.me (2015-04-14T22:09:53.904Z)
Axel Rauschmayer wrote: > Given that redundant calls to `return()` don’t make a difference (h/t Bergi) I'm sorry, that was not 100% accurate. I only referred to `.return(x)` returning {done:true, value:x} and `.throw(e)` being equivalent to `throw e;` when the generator was never started or is already completed (http://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresumeabrupt). In fact, there are generators that behave differently when being prematurely aborted and attempted to be closed multiple times. A contrived example: ```js function* unstoppableCounter(n) { try { while (true) yield n++; } finally { console.log("unclosable!"); yield* unstoppableCounter(n); } } var counter = unstoppableCounter(0); var i=5; for (var x of counter) { console.log(x); if (!--i) break; } i=4; for (var x of counter) { console.log(x); if (!--i) break; } ``` Every call of `counter.return()` here would actually log "unclosable!", increase the counter and yield {done:false, value:…} - in general, might have side effects. So we shouldn't do it arbitrarily often. > couldn’t the iteration protocol be simpler if iterators were always closed. “Manually implemented” iterators could be written without the check in line (A) I don't think that's how an explicit iterator would be written. Shouldn't it look more like … next() { if (iterationIsDone()) { cleanUp(); return {done: true}; } else { return {value: nextValue(), done: false}; } } Of course that assumes that we don't have a return value, and `next()` is no more called after it returned `done: true` once (otherwise we'd clean up multiple times). Maybe better: … next() { if (iterationIsDone()) { return {done: true}; } else { let result = {value: nextValue(), done: iterationIsDone()}; if (result.done) cleanUp(); // (B) return result; } } Admittedly, that has the line you argued against again…