Once upon a time in a cozy little code kitchen, there was a magical cookie maker. But it wasnโ€™t your usual cookie machine. No no! This one had superpowersโ€ฆ

It didnโ€™t dump all the cookies out at once like most machines.

Instead, it had a secret: it could pause and wait, give you a cookie one by one, and continue only when you were ready!

๐Ÿง™โ€โ™‚๏ธ Meet the Wizard Function*

In JavaScript, we tell the computer, โ€œHey, this is going to be a magical cookie maker!โ€ by writing:

function* cookieMaker() {
  yield "Chocolate Chip Cookie ๐Ÿช";
  yield "Oatmeal Raisin Cookie ๐Ÿช";
  yield "Peanut Butter Cookie ๐Ÿช";
}

Notice that little star (*) after function? Thatโ€™s the twinkle of magic. It tells JavaScript, โ€œThis is not just any function. This is a generator โ€” a cookie maker that gives cookies slowly!โ€

Now, when we start this generator, it doesnโ€™t immediately give us all three cookies.

It gives us something better: ๐ŸŽฎ A remote control (called an iterator).

const cookies = cookieMaker();

Now cookies holds our magical remote, and we can press the next() button whenever we want to get a cookie!


Letโ€™s press the button!

console.log(cookies.next());

๐ŸŽ‰ Output:

{ value: 'Chocolate Chip Cookie ๐Ÿช', done: false }

โ€œHereโ€™s your first cookie! Iโ€™ll wait right here until you ask for the next one.โ€

When we press again:

console.log(cookies.next());

Output:

{ value: 'Oatmeal Raisin Cookie ๐Ÿช', done: false }

And again:

console.log(cookies.next());

Output:

{ value: 'Peanut Butter Cookie ๐Ÿช', done: false }

One more time?

console.log(cookies.next());

Output:

{ value: undefined, done: true }

๐Ÿ˜ข โ€œNo more cookies! Iโ€™m all done.โ€


Letโ€™s say you only want one cookie and no more. You can politely tell the cookie maker:

cookies.return("Iโ€™m full!");

This will return:

{ value: "Iโ€™m full!", done: true }

And the machine is now officially retired. It wonโ€™t make any more cookies.


Imagine one cookie got burnt!

You can tell the cookie maker:

cookies.throw(new Error("Burnt cookie!"));

The generator will stop immediately and JavaScript will treat this as an error โ€” unless you catch it.


๐Ÿง Adding Ingredients with next(value)

Now hereโ€™s something REALLY magical.

Letโ€™s say the cookie maker pauses and asks: โ€œWhat flavor should the next cookie be?โ€

You can send it an ingredient like this:

function* customCookieMaker() {
  const flavor = yield "What flavor do you want?";
  yield `Hereโ€™s your ${flavor} cookie! ๐Ÿช`;
}

const custom = customCookieMaker();
console.log(custom.next());            // Ask: What flavor?
console.log(custom.next("Strawberry")); // Answer: Strawberry

Output:

{ value: 'What flavor do you want?', done: false }
{ value: 'Hereโ€™s your Strawberry cookie! ๐Ÿช', done: false }

Whoa! We gave it a flavor, and it baked the cookie using our input!


๐Ÿง‘โ€๐Ÿณ Teamwork with yield* โ€“ Let the Sprinkle Chef Help

Sometimes, our cookie maker might say:

โ€œI need help adding sprinkles. Let my sprinkle chef do that.โ€

So we use:

function* sprinkleChef() {
  yield "Rainbow Sprinkles ๐ŸŒˆ";
  yield "Chocolate Sprinkles ๐Ÿซ";
}

function* bigChef() {
  yield "Mixing Dough...";
  yield* sprinkleChef(); // Let the sprinkle chef do his job
  yield "Baking Cookies...";
}

When we run bigChef(), weโ€™ll get all the steps โ€” including those from sprinkleChef โ€” in order!


๐Ÿ”„ Want All Cookies at Once? Use a for...of Loop!

Tired of pressing next() again and again? ๐Ÿ˜… Just say:

for (let cookie of cookieMaker()) {
  console.log(cookie);
}

Output:

Chocolate Chip Cookie ๐Ÿช
Oatmeal Raisin Cookie ๐Ÿช
Peanut Butter Cookie ๐Ÿช

So neat! The for...of loop presses next() for you automatically โ€” until it gets to done: true.


๐Ÿ“ฆ Waiting for Ingredients โ€” Async Magic

Letโ€™s pretend the cookie maker needs to wait for chocolate chips to be delivered online. You donโ€™t want your whole kitchen to freeze while waiting.

Generators were the first way JavaScript handled pauses in time without freezing.

These days, JavaScript uses async and await for that. But guess what? async/await was inspired by generators!

async function bakeCookies() {
  const delivery = await getChocolateChips();
  console.log(`Baking with ${delivery}!`);
}

Just like a smart generator, async functions also pause and resume when the data arrives.


๐Ÿ“ Review Questions (Practice Time!)

  1. ๐Ÿง What does the * in function* mean in JavaScript?
  2. ๐Ÿช What happens when a generator reaches a yield keyword?
  3. ๐ŸŽฎ What does calling .next() on a generator return?
  4. ๐Ÿ›‘ How do you tell a generator to stop early and return a final value?
  5. ๐Ÿซ What does yield* allow one generator to do with another?

<
Previous Post
๐Ÿงญ The Algorithm Adventure of Captain Flowchart
>
Next Post
๐ŸŽจ Personal Blog: The Day My 6-Year-Old Taught Me Colours