Methods can be generators (*name(), Generators), async (Promises and Asynchrony) or both. A generator named [Symbol.iterator] makes instances iterable. A subclass without a constructor gets constructor(...args) { super(...args); } automatically.
class Playlist {
constructor(...songs) { this.songs = songs; }
*[Symbol.iterator]() { yield* this.songs; } // generator method: iterable
async *stream() { for (const s of this) yield await s; } // async generator method
async first() { return (await this.stream().next()).value; } // async method
}
class Mix extends Playlist {} // implicit constructor(...args) { super(...args); }
const mix = new Mix('Intro', 'Outro'), streamed = [];
for await (const song of mix.stream()) streamed.push(song);
console.log(streamed, [...mix], await mix.first());[ 'Intro', 'Outro' ] [ 'Intro', 'Outro' ] Intro