生成器(Generator)和迭代器(Iterator)是 ES6 引入的强大特性,用于创建可迭代对象和控制函数执行流程。
迭代器是一个对象,实现了 Iterator 协议,包含 next() 方法。
const iterator = {
values: [1, 2, 3],
index: 0,
next() {
if (this.index < this.values.length) {
return { value: this.values[this.index++], done: false };
}
return { done: true };
}
};
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { done: true }
实现了 Symbol.iterator 方法的对象,返回一个迭代器。
const iterable = {
values: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.values.length) {
return { value: this.values[index++], done: false };
}
return { done: true };
}
};
}
};
for (const value of iterable) {
console.log(value); // 1, 2, 3
}
使用 function* 定义生成器函数,使用 yield 暂停执行。
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = numberGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
function* generator() {
yield 1;
yield 2;
return 3; // return 会结束生成器
}
const gen = generator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: true }
function* generator1() {
yield 1;
yield 2;
}
function* generator2() {
yield* generator1();
yield 3;
}
const gen = generator2();
console.log([...gen]); // [1, 2, 3]
function* generator() {
const x = yield 1;
const y = yield x + 2;
return y;
}
const gen = generator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next(10)); // { value: 12, done: false } (x = 10)
console.log(gen.next(20)); // { value: 20, done: true } (y = 20)
function* fibonacci() {
let [prev, curr] = [0, 1];
while (true) {
yield curr;
[prev, curr] = [curr, prev + curr];
}
}
const fib = fibonacci();
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2
console.log(fib.next().value); // 3
console.log(fib.next().value); // 5
function* traverseTree(node) {
yield node.value;
if (node.left) {
yield* traverseTree(node.left);
}
if (node.right) {
yield* traverseTree(node.right);
}
}
const tree = {
value: 1,
left: {
value: 2,
left: { value: 4 },
right: { value: 5 }
},
right: {
value: 3,
left: { value: 6 },
right: { value: 7 }
}
};
for (const value of traverseTree(tree)) {
console.log(value); // 1, 2, 4, 5, 3, 6, 7
}
function* asyncGenerator() {
const result1 = yield fetch('/api/data1');
const data1 = yield result1.json();
const result2 = yield fetch('/api/data2');
const data2 = yield result2.json();
return { data1, data2 };
}
// 执行器
function run(generator) {
const gen = generator();
function handle(result) {
if (result.done) return Promise.resolve(result.value);
return Promise.resolve(result.value)
.then(res => handle(gen.next(res)))
.catch(err => handle(gen.throw(err)));
}
return handle(gen.next());
}
run(asyncGenerator()).then(data => console.log(data));
function* stateMachine() {
let state = 'idle';
while (true) {
const action = yield state;
switch (state) {
case 'idle':
if (action === 'start') state = 'running';
break;
case 'running':
if (action === 'stop') state = 'stopped';
break;
case 'stopped':
if (action === 'reset') state = 'idle';
break;
}
}
}
const machine = stateMachine();
console.log(machine.next().value); // 'idle'
console.log(machine.next('start').value); // 'running'
console.log(machine.next('stop').value); // 'stopped'
console.log(machine.next('reset').value); // 'idle'
function* dataProcessor() {
let data = yield;
while (true) {
data = yield data * 2;
}
}
const processor = dataProcessor();
processor.next(); // 启动生成器
console.log(processor.next(5).value); // 10
console.log(processor.next(10).value); // 20
console.log(processor.next(15).value); // 30
next(value)
return(value)
function* generator() {
yield 1;
yield 2;
yield 3;
}
const gen = generator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.return(100)); // { value: 100, done: true }
console.log(gen.next()); // { value: undefined, done: true }
function* generator() {
try {
yield 1;
yield 2;
} catch (error) {
console.log('Caught:', error);
yield 3;
}
}
const gen = generator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.throw('error')); // Caught: error, { value: 3, done: false }
数组、字符串、Map、Set 等都是可迭代的:
// 数组
for (const item of [1, 2, 3]) {
console.log(item);
}
// 字符串
for (const char of 'hello') {
console.log(char);
}
// Map
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {
console.log(key, value);
}
// Set
const set = new Set([1, 2, 3]);
for (const value of set) {
console.log(value);
}
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
*[Symbol.iterator]() {
for (let i = this.start; i <= this.end; i++) {
yield i;
}
}
}
const range = new Range(1, 5);
console.log([...range]); // [1, 2, 3, 4, 5]
// 实现迭代器协议
const myIterable = {
[Symbol.iterator]() {
let step = 0;
return {
next() {
step++;
if (step === 1) {
return { value: 'hello', done: false };
} else if (step === 2) {
return { value: 'world', done: false };
}
return { done: true };
}
};
}
};
for (const value of myIterable) {
console.log(value); // 'hello', 'world'
}
function* gen() {
yield 1;
}
const g = gen(); // 返回生成器对象,不执行函数体
console.log(g.next()); // 执行并返回 { value: 1, done: false }
function* gen() {
yield 1;
yield 2;
}
const g = gen();
console.log([...g]); // [1, 2]
console.log([...g]); // [] (已耗尽)
function normal() {
yield 1; // SyntaxError: Unexpected strict mode reserved word
}
function* gen() {
console.log('Started');
yield 1;
console.log('Ended');
}
const g = gen(); // 不输出任何内容
g.next(); // 输出: Started
g.next(); // 输出: Ended
async/await 基于生成器和 Promise:
// async/await (语法糖)
async function fetchData() {
const data = await fetch('/api/data');
return data.json();
}
// 使用生成器实现类似功能
function* fetchDataGen() {
const data = yield fetch('/api/data');
return data.json();
}