二、Generator 基础
2.1 基本用法
function* simpleGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = simpleGenerator();
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 }
2.2 next() 方法详解
function* generatorWithReturn() {
yield '第一次暂停';
yield '第二次暂停';
return '完成';
}
const gen = generatorWithReturn();
console.log(gen.next()); // { value: '第一次暂停', done: false }
console.log(gen.next()); // { value: '第二次暂停', done: false }
console.log(gen.next()); // { value: '完成', done: true }
console.log(gen.next()); // { value: undefined, done: true }
2.3 传递参数给 next()
function* generatorWithInput() {
const input1 = yield '等待输入 1';
console.log('收到输入 1:', input1);
const input2 = yield '等待输入 2';
console.log('收到输入 2:', input2);
return '完成';
}
const gen = generatorWithInput();
console.log(gen.next()); // { value: '等待输入 1', done: false }
console.log(gen.next('Hello')); // { value: '等待输入 2', done: false }
console.log(gen.next('World')); // { value: '完成', done: true }
三、Generator 的内部机制
3.1 执行上下文栈
Generator 暂停时会保存执行上下文:
function* countGenerator() {
console.log('开始');
yield 1;
console.log('继续');
yield 2;
console.log('结束');
}
const gen = countGenerator();
gen.next(); // 开始
gen.next(); // 继续
gen.next(); // 结束
3.2 状态机
Generator 可以看作状态机:
// Generator 的状态转换
// S0: 初始状态
// S1: 第一次 yield 后
// S2: 第二次 yield 后
// S3: 完成状态
四、Iterator 协议
4.1 Iterator 接口
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
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()); // { value: undefined, done: true }
4.2 自定义 Iterator
const customIterator = {
[Symbol.iterator]() {
let count = 0;
return {
next() {
if (count < 3) {
return { value: count++, done: false };
}
return { value: undefined, done: true };
}
};
}
};
for (const num of customIterator) {
console.log(num); // 0, 1, 2
}
4.3 Generator 返回 Iterator
function* generator() {
yield 1;
yield 2;
yield 3;
}
const gen = generator();
console.log(gen[Symbol.iterator]() === gen); // true
for (const num of generator()) {
console.log(num); // 1, 2, 3
}
五、Generator 的高级用法
5.1 yield* 委托
function* innerGenerator() {
yield 'a';
yield 'b';
}
function* outerGenerator() {
yield 'start';
yield* innerGenerator();
yield 'end';
}
for (const value of outerGenerator()) {
console.log(value); // start, a, b, end
}
5.2 return() 方法
function* generator() {
try {
yield 1;
yield 2;
yield 3;
} finally {
console.log('finally 执行');
}
}
const gen = generator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.return('提前结束')); // { value: '提前结束', done: true }
console.log(gen.next()); // { value: undefined, done: true }
5.3 throw() 方法
function* generator() {
try {
yield '正常执行';
yield '继续执行';
} catch (e) {
console.log('捕获异常:', e);
}
}
const gen = generator();
console.log(gen.next());
console.log(gen.throw(new Error('出错了')));
六、Generator 与异步编程
6.1 回调地狱问题
// 回调地狱
getData(function(data1) {
getMoreData(data1, function(data2) {
getEvenMoreData(data2, function(data3) {
console.log(data3);
});
});
});
6.2 使用 Generator 简化异步
function* asyncGenerator() {
const data1 = yield getData();
const data2 = yield getMoreData(data1);
const data3 = yield getEvenMoreData(data2);
console.log(data3);
}
// 执行器
function run(generator) {
const gen = generator();
function handle(result) {
if (result.done) return;
result.value.then(data => {
handle(gen.next(data));
});
}
handle(gen.next());
}
run(asyncGenerator);
6.3 Thunk 函数
// Thunk 函数
function thunkify(fn) {
return function(...args) {
return function(callback) {
return fn.call(this, ...args, callback);
};
};
}
// 使用
const getDataThunk = thunkify(getData);
const thunk = getDataThunk(arg1, arg2);
thunk(callback);
七、深入理解协程
7.1 协程概念
协程是一种比线程更轻量的并发执行单位,可以暂停和恢复。
function* task1() {
console.log('任务 1 开始');
yield;
console.log('任务 1 继续');
}
function* task2() {
console.log('任务 2 开始');
yield;
console.log('任务 2 继续');
}
const t1 = task1();
const t2 = task2();
t1.next();
t2.next();
t1.next();
t2.next();
7.2 Generator 实现协程
function* producer() {
for (let i = 0; i < 3; i++) {
yield i;
}
}
function* consumer() {
for (const value of producer()) {
console.log('消费:', value);
}
}
for (const _ of consumer()) {}
八、实战案例
8.1 无限序列生成器
function* infiniteSequence() {
let i = 0;
while (true) {
yield i++;
}
}
const gen = infiniteSequence();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
8.2 斐波那契数列
function* fibonacci() {
let [prev, curr] = [0, 1];
while (true) {
yield curr;
[prev, curr] = [curr, prev + curr];
}
}
const fib = fibonacci();
for (let i = 0; i < 10; i++) {
console.log(fib.next().value);
}
8.3 异步任务调度器
function* taskRunner(tasks) {
for (const task of tasks) {
yield task();
}
}
const tasks = [
() => Promise.resolve('任务 1'),
() => Promise.resolve('任务 2'),
() => Promise.resolve('任务 3')
];
function runTasks(generator) {
const gen = generator();
function handle(result) {
if (result.done) return;
result.value.then(data => {
console.log(data);
handle(gen.next());
});
}
handle(gen.next());
}
runTasks(() => taskRunner(tasks));
九、async/await 与 Generator 的关系
9.1 async/await 是 Generator 的语法糖
// Generator 方式
function* fetchData() {
const data1 = yield fetch('/api/data1');
const data2 = yield fetch('/api/data2');
return [data1, data2];
}
// async/await 方式
async function fetchData() {
const data1 = await fetch('/api/data1');
const data2 = await fetch('/api/data2');
return [data1, data2];
}
9.2 实现简易的 async/await
function spawn(genF) {
return new Promise((resolve, reject) => {
const gen = genF();
function step(nextF) {
let next;
try {
next = nextF();
} catch (e) {
return reject(e);
}
if (next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(
v => step(() => gen.next(v)),
e => step(() => gen.throw(e))
);
}
step(() => gen.next());
});
}