const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); async function* delayedRange() { try { for(let i = 1; i <= 1000; i++) { await delay(100); yield i; } } catch (e) { console.log("Oh no error in iterator", e); } finally { console.log("OMG, finally blocks run on termination"); } } const range = delayedRange(); console.log(await range.next()); // { value: 1, done: false } console.log(await range.next()); // { value 2, done: false } // range.return(); // this runs the `finally` block, logs and finishes the generator. range.throw(new RangeError('fun')); // this runs *both* the catch and the finally // In a for await loop, `break`ing the loop calls `.return` implicitly so loops support cancellation // In a for await loop, `throw`ing similarly propagates the error - funnily it's just this way because // cancellation progress stopped "mid way" and because of the duality proposal.