Generator(生成器)是 ES6 引入的一种特殊的函数,可以暂停和恢复执行,是 JavaScript 中实现协程的一种方式。
Generator 函数使用 function*
语法定义:
function* generatorFunction() {
yield 'Hello';
yield 'World';
}
调用 Generator 函数不会立即执行函数体,而是返回一个 Generator 对象:
const generator = generatorFunction();
Generator 对象遵循迭代器协议,可以使用 next()
方法获取值:
console.log(generator.next()); // { value: 'Hello', done: false }
console.log(generator.next()); // { value: 'World', done: false }
console.log(generator.next()); // { value: undefined, done: true }
yield
是 Generator 函数的核心特性:
yield
表达式时,函数执行暂停next()
时,从暂停处继续执行yield
后面的表达式会被求值并作为 next()
返回对象的 value
属性可以向 next()
传递参数,这个参数会成为上一个 yield
表达式的返回值:
function* gen() {
const x = yield 'Hello';
const y = yield x + ' World';
return y;
}
const g = gen();
console.log(g.next()); // { value: 'Hello', done: false }
console.log(g.next(42)); // { value: '42 World', done: false }
console.log(g.next(100)); // { value: 100, done: true }
终止 Generator 执行:
function* gen() {
yield 1;
yield 2;
yield 3;
}
const g = gen();
console.log(g.next()); // { value: 1, done: false }
console.log(g.return('foo')); // { value: 'foo', done: true }
console.log(g.next()); // { value: undefined, done: true }
在 Generator 内部抛出错误:
function* gen() {
try {
yield 1;
yield 2;
} catch (e) {
console.log('Error caught:', e);
}
}
const g = gen();
console.log(g.next()); // { value: 1, done: false }
console.log(g.throw(new Error('Something went wrong')));
// Error caught: Error: Something went wrong
// { value: undefined, done: true }
Generator 可以简化迭代器的实现:
function* range(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
for (const num of range(1, 5)) {
console.log(num); // 1, 2, 3, 4, 5
}
Generator 可以用来管理异步流程(通常与 Promise 配合使用):
function* asyncGenerator() {
const result1 = yield fetch('https://api.example.com/data1');
const result2 = yield fetch('https://api.example.com/data2');
return [result1, result2];
}
// 需要一个执行器来运行这个生成器
async function runGenerator(generator) {
const iterator = generator();
let result;
try {
while (true) {
const { value, done } = iterator.next(result);
if (done) return value;
result = await value;
}
} catch (err) {
iterator.throw(err);
}
}
runGenerator(asyncGenerator).then(console.log);
Generator 可以表示无限序列:
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
// 可以无限继续...
Generator 可以用于管理复杂的状态机:
function* trafficLight() {
while (true) {
yield 'red';
yield 'yellow';
yield 'green';
yield 'yellow';
}
}
const light = trafficLight();
console.log(light.next().value); // 'red'
console.log(light.next().value); // 'yellow'
console.log(light.next().value); // 'green'
// 可以无限循环...
yield*
用于委托给另一个 Generator 或可迭代对象:
function* generator1() {
yield 2;
yield 3;
}
function* generator2() {
yield 1;
yield* generator1();
yield 4;
}
const gen = generator2();
console.log([...gen]); // [1, 2, 3, 4]
也可以委托给数组、字符串等可迭代对象:
function* gen() {
yield* [1, 2, 3];
yield* 'hello';
}
console.log([...gen()]); // [1, 2, 3, 'h', 'e', 'l', 'l', 'o']
yield
,会报错new
一起使用Generator 为 JavaScript 带来了更强大的控制流程能力,特别是在处理异步操作和复杂迭代时提供了更优雅的解决方案。虽然现代 JavaScript 中 async/await 已经很大程度上取代了 Generator 在异步编程中的角色,但 Generator 仍然在许多场景下非常有用。