Let’s look at a real-life example where two-way communication with generators makes sense. One practical use case is simulating a state machine, where the state of the system can change based on inputs.
Example: Vending Machine Simulation
In this example, we’ll create a simple vending machine generator where the user inputs money. The machine keeps track of the total amount and either dispenses an item or asks for more money depending on how much the user has inserted.
Here’s how it can work:
function* vendingMachine() {
let totalAmount = 0;
const itemPrice = 50;
while (true) {
const money = yield `You have inserted ${totalAmount} cents. Insert more or type "dispense" to get the item.`;
if (money === "dispense") {
if (totalAmount >= itemPrice) {
yield `Item dispensed! Remaining change: ${totalAmount - itemPrice} cents.`;
totalAmount = 0; // Reset for the next transaction
} else {
yield `Not enough money. You need ${itemPrice - totalAmount} more cents.`;
}
} else if (typeof money === 'number') {
totalAmount += money;
yield `You have inserted ${money} cents. Total: ${totalAmount} cents.`;
} else {
yield "Invalid input. Please insert money or type 'dispense'.";
}
}
}
const machine = vendingMachine();
console.log(machine.next().value); // Start the machine, initial prompt
console.log(machine.next(20).value); // User inserts 20 cents
console.log(machine.next(30).value); // User inserts 30 more cents
console.log(machine.next("dispense").value); // User asks for the item
console.log(machine.next().value); // Reset, new prompt
Step-by-Step Explanation:
- Start the Machine: The first
machine.next()
call starts the vending machine. It returns a message telling the user how much they’ve inserted so far ("You have inserted 0 cents..."
). - Insert Money (First Time):
- The second call,
machine.next(20)
, sends20
into the generator, which gets added to the total amount. - The generator yields the message,
"You have inserted 20 cents. Total: 20 cents."
- Insert More Money (Second Time):
- The third call,
machine.next(30)
, sends30
into the generator. This gets added to the previous total (20 cents), making the total50
cents. - The generator responds with
"You have inserted 30 cents. Total: 50 cents."
- Dispense Item:
- Now, the user calls
machine.next("dispense")
, asking the machine to dispense the item. - Since the total amount (
50
) is enough to buy the item, the machine dispenses it and returns the message"Item dispensed! Remaining change: 0 cents."
- The total amount is then reset to
0
for the next transaction.
- Reset for New Transaction: After dispensing the item, the generator loops back and resets, ready for the next input. The vending machine is now in a state where it can take new money for a future purchase.
Why Use Two-Way Communication Here?
- Receiving input: The generator receives values each time the user inserts money or requests the item (
next(value)
). - Responding based on input: The generator dynamically adjusts its behavior based on whether the input is money (which adds to the total) or a request to dispense (which checks if the total is enough to give the item).
- Managing state: The
totalAmount
tracks how much money has been inserted, allowing the machine to remember the state between each user interaction.
A User Interaction Example:
> console.log(machine.next().value); // You have inserted 0 cents. Insert more...
> console.log(machine.next(20).value); // You have inserted 20 cents. Total: 20 cents.
> console.log(machine.next(30).value); // You have inserted 30 cents. Total: 50 cents.
> console.log(machine.next("dispense").value); // Item dispensed! Remaining change: 0 cents.
> console.log(machine.next().value); // You have inserted 0 cents. Insert more...
The generator keeps track of state and updates the user accordingly with each new interaction. The user can insert more money or try to dispense the item, and the generator adjusts based on the current state (totalAmount
).
Why Use Generators in This Case?
- Statefulness: The generator maintains the state (
totalAmount
) across multiple steps in an elegant way. - Two-way communication: It allows for both input (
next(value)
) and output (yield "message"
), making the vending machine’s behavior dynamic and reactive to the user’s actions. - Infinite loop: The
while (true)
loop ensures that the vending machine keeps running, ready to handle more transactions, without needing to be restarted every time.
Benefits of This Approach:
- Simplicity: The generator provides a clean and simple way to handle state transitions.
- Interactivity: The two-way communication makes the system interactive, with the generator responding differently based on the input at each step.
This vending machine example is a great way to see how generators can be used in real-world applications to handle state, interactivity, and two-way communication.