JavaScript Generators Tutorial - Section 2: Fundamentals
The yield keyword is not just a statement - it's an expression that can receive values from the caller.
function* twoWay() {
const x = yield 'First';
console.log('Received:', x);
const y = yield 'Second';
console.log('Received:', y);
return 'Done';
}
const gen = twoWay();
console.log(gen.next()); // { value: 'First', done: false }
console.log(gen.next(10)); // Logs: Received: 10
// { value: 'Second', done: false }
console.log(gen.next(20)); // Logs: Received: 20
// { value: 'Done', done: true }
function* communicator() {
console.log('Started');
const a = yield 1;
console.log('Got a:', a);
const b = yield 2;
console.log('Got b:', b);
const c = yield 3;
console.log('Got c:', c);
}
const gen = communicator();
// First next() starts the generator
console.log(gen.next()); // Logs: Started
// Returns: { value: 1, done: false }
// Second next() sends value to first yield
console.log(gen.next('hello')); // Logs: Got a: hello
// Returns: { value: 2, done: false }
// Third next() sends value to second yield
console.log(gen.next('world')); // Logs: Got b: world
// Returns: { value: 3, done: false }
console.log(gen.next('!')); // Logs: Got c: !
// Returns: { value: undefined, done: true }
function* trafficLight() {
while (true) {
console.log('🔴 Red');
const redDuration = yield 'red';
console.log('🟢 Green');
const greenDuration = yield 'green';
console.log('🟡 Yellow');
const yellowDuration = yield 'yellow';
}
}
const light = trafficLight();
light.next(); // 🔴 Red
light.next(3000); // 🟢 Green (red was 3s)
light.next(5000); // 🟡 Yellow (green was 5s)
light.next(1000); // 🔴 Red (yellow was 1s)
function* calculator() {
let result = 0;
while (true) {
const operation = yield result;
if (!operation) continue;
const [op, value] = operation.split(' ');
const num = parseFloat(value);
switch (op) {
case 'add':
result += num;
break;
case 'subtract':
result -= num;
break;
case 'multiply':
result *= num;
break;
case 'divide':
result /= num;
break;
case 'reset':
result = 0;
break;
}
}
}
const calc = calculator();
calc.next(); // Start: { value: 0, done: false }
console.log(calc.next('add 10').value); // 10
console.log(calc.next('multiply 2').value); // 20
console.log(calc.next('subtract 5').value); // 15
console.log(calc.next('divide 3').value); // 5
console.log(calc.next('reset').value); // 0
function* withDefaults() {
const a = (yield 'Enter A') || 'default A';
console.log('A:', a);
const b = (yield 'Enter B') || 'default B';
console.log('B:', b);
}
const gen = withDefaults();
gen.next(); // { value: 'Enter A', done: false }
gen.next(); // Logs: A: default A (undefined || 'default A')
gen.next('custom'); // Logs: B: custom
function* accumulator(initial = 0) {
let total = initial;
while (true) {
const value = yield total;
if (value !== undefined) {
total += value;
}
}
}
const sum = accumulator(0);
console.log(sum.next().value); // 0
console.log(sum.next(5).value); // 5
console.log(sum.next(10).value); // 15
console.log(sum.next(3).value); // 18
console.log(sum.next().value); // 18 (no change)
function* dataProcessor() {
while (true) {
const query = yield 'Ready';
if (query === 'status') {
yield 'Processing...';
} else if (query === 'data') {
yield { items: [1, 2, 3], count: 3 };
} else if (query === 'error') {
yield new Error('Something went wrong');
} else {
yield 'Unknown command';
}
}
}
const processor = dataProcessor();
processor.next(); // Start
console.log(processor.next('status').value); // 'Processing...'
processor.next(); // Continue
console.log(processor.next('data').value); // { items: [1, 2, 3], count: 3 }
processor.next(); // Continue
console.log(processor.next('error').value); // Error: Something went wrong
function* formWizard() {
const name = yield { step: 1, question: 'What is your name?' };
console.log('Name:', name);
const email = yield { step: 2, question: 'What is your email?' };
console.log('Email:', email);
const age = yield { step: 3, question: 'What is your age?' };
console.log('Age:', age);
return {
name,
email,
age,
completed: true
};
}
const wizard = formWizard();
console.log(wizard.next().value);
// { step: 1, question: 'What is your name?' }
console.log(wizard.next('John Doe').value);
// Logs: Name: John Doe
// { step: 2, question: 'What is your email?' }
console.log(wizard.next('john@example.com').value);
// Logs: Email: john@example.com
// { step: 3, question: 'What is your age?' }
console.log(wizard.next(30).value);
// Logs: Age: 30
// { name: 'John Doe', email: 'john@example.com', age: 30, completed: true }
function* validator() {
while (true) {
const input = yield 'Enter value';
if (typeof input === 'number' && input > 0) {
yield { valid: true, value: input };
} else {
yield { valid: false, error: 'Must be a positive number' };
}
}
}
const val = validator();
val.next(); // Start
console.log(val.next(10).value); // { valid: true, value: 10 }
val.next(); // Continue
console.log(val.next(-5).value); // { valid: false, error: '...' }
val.next(); // Continue
console.log(val.next('abc').value); // { valid: false, error: '...' }
yield is an expression that can receive valuesnext(value) to send values into generatornext() starts generator, can't pass valueNow let's explore generator methods like return() and throw()!