JavaScript中的Generator函数是一种可以暂停执行、后续再恢复执行的函数,通过function*语法定义,内部使用yield关键字暂停执行,外部通过next方法恢复执行并获取返回值。它和普通函数的执行逻辑有本质区别,因此在很多特定场景下能发挥独特作用。

Generator函数基础回顾
Generator函数执行后不会立即执行函数体内部代码,而是返回一个迭代器对象,每次调用迭代器的next方法,函数会从上次暂停的位置继续执行,直到遇到下一个yield或者函数结束。我们可以通过一个简单的示例理解它的基础用法:
// 定义Generator函数
function* simpleGenerator() {
console.log('第一次执行');
yield 1;
console.log('第二次执行');
yield 2;
console.log('第三次执行');
return 3;
}
// 获取迭代器对象
const iterator = simpleGenerator();
// 第一次调用next,执行到第一个yield暂停
console.log(iterator.next()); // 输出:第一次执行 { value: 1, done: false }
// 第二次调用next,从上次暂停位置继续执行到第二个yield
console.log(iterator.next()); // 输出:第二次执行 { value: 2, done: false }
// 第三次调用next,执行到return结束
console.log(iterator.next()); // 输出:第三次执行 { value: 3, done: true }
Generator函数的不可替代应用场景
1. 简化复杂异步流程控制
在Promise出现之前,异步操作的处理很容易出现回调地狱,而Generator函数配合执行器可以实现类似async/await的同步写法处理异步,比嵌套回调可读性高很多。即使现在有了async/await,在一些需要自定义异步执行逻辑的场景下,Generator依然有独特优势。
比如我们需要按顺序执行多个异步请求,且前一个请求的结果要作为后一个请求的参数,用Generator实现如下:
// 模拟异步请求函数
function mockRequest(url, params) {
return new Promise(resolve => {
setTimeout(() => {
resolve(`请求${url}完成,参数:${JSON.stringify(params)}`);
}, 1000);
});
}
// 定义Generator函数处理异步流程
function* asyncFlow() {
const result1 = yield mockRequest('/api/user', { id: 1 });
console.log(result1);
const result2 = yield mockRequest('/api/order', { userId: 1 });
console.log(result2);
const result3 = yield mockRequest('/api/goods', { orderId: 1001 });
console.log(result3);
return '所有请求完成';
}
// 自定义Generator执行器
function runGenerator(gen) {
const iterator = gen();
function handle(result) {
if (result.done) {
return result.value;
}
// 如果yield后面是Promise,等待Promise完成后再继续执行
return result.value.then(data => {
return handle(iterator.next(data));
});
}
return handle(iterator.next());
}
// 执行异步流程
runGenerator(asyncFlow).then(finalResult => {
console.log(finalResult);
});
这种写法把异步操作的逻辑用同步的方式表达,比嵌套的then链式调用更直观,而且执行器的逻辑可以自定义,比如添加错误捕获、请求超时控制等,灵活性比固定的async/await更高。
2. 实现自定义迭代器
JavaScript中很多内置对象都具备迭代性,比如数组、字符串、Map、Set等,但是如果我们想让自定义的对象也支持for...of遍历,就需要实现迭代器协议。Generator函数可以快速生成符合迭代器协议的对象,比手动实现next方法要简单很多。
比如我们需要实现一个可以遍历斐波那契数列的自定义对象:
// 定义生成斐波那契数列的Generator函数
function* fibonacciGenerator(max) {
let a = 0, b = 1;
while (a <= max) {
yield a;
[a, b] = [b, a + b];
}
}
// 自定义可迭代对象
const fibonacciObj = {
max: 100,
[Symbol.iterator]: function() {
return fibonacciGenerator(this.max);
}
};
// 使用for...of遍历
for (const num of fibonacciObj) {
console.log(num); // 输出:0 1 1 2 3 5 8 13 21 34 55 89
}
如果手动实现迭代器,需要定义next方法,处理value和done的逻辑,而用Generator函数只需要关注每次迭代要返回的值即可,大大降低了自定义迭代器的实现成本。
3. 构建轻量级状态机
状态机是指系统有多个状态,并且可以根据输入在不同状态之间切换的场景,比如游戏角色的状态切换、订单状态流转等。Generator函数的暂停和恢复特性天然适合实现状态机,每个yield对应一个状态,外部调用next方法触发状态切换,比用switch-case或者状态模式实现更简洁。
以简单的订单状态流转为例,订单状态有:待支付、已支付、已发货、已收货、已完成,实现如下:
// 定义订单状态机Generator函数
function* orderStateMachine() {
let state = '待支付';
while (true) {
const action = yield state;
switch (action) {
case '支付':
state = '已支付';
break;
case '发货':
if (state === '已支付') {
state = '已发货';
}
break;
case '确认收货':
if (state === '已发货') {
state = '已收货';
}
break;
case '完成':
if (state === '已收货') {
state = '已完成';
}
break;
default:
break;
}
}
}
// 初始化状态机
const orderSM = orderStateMachine();
// 获取初始状态
console.log(orderSM.next().value); // 输出:待支付
// 触发状态切换
console.log(orderSM.next('支付').value); // 输出:已支付
console.log(orderSM.next('发货').value); // 输出:已发货
console.log(orderSM.next('确认收货').value); // 输出:已收货
console.log(orderSM.next('完成').value); // 输出:已完成
这种实现方式把状态流转的逻辑封装在Generator函数内部,外部只需要调用next传入动作即可,状态的管理逻辑非常清晰,避免了分散的状态判断代码。
4. 控制并发任务执行
在一些需要控制并发数量的场景下,比如同时发起多个请求但是最多只能有3个请求同时执行,Generator函数可以配合队列实现并发控制,比用Promise.all配合复杂的计数逻辑更简单。
实现并发控制的示例如下:
// 模拟单个异步任务
function task(id) {
return new Promise(resolve => {
setTimeout(() => {
console.log(`任务${id}完成`);
resolve(id);
}, Math.random() * 2000 + 1000);
});
}
// 定义Generator函数生成所有任务
function* taskGenerator() {
for (let i = 1; i <= 10; i++) {
yield task(i);
}
}
// 并发控制函数,maxConcurrent为最大并发数
function concurrentControl(gen, maxConcurrent) {
const iterator = gen();
const running = []; // 存储正在执行的任务
function run() {
// 如果正在执行的任务数小于最大并发数,且还有未执行的任务,就启动新任务
while (running.length < maxConcurrent) {
const result = iterator.next();
if (result.done) {
break;
}
// 启动任务,任务完成后从running中移除,并继续启动新任务
const promise = result.value.then(data => {
const index = running.indexOf(promise);
running.splice(index, 1);
run();
return data;
});
running.push(promise);
}
// 如果所有任务都完成了,返回所有结果
if (running.length === 0 && iterator.next().done) {
return Promise.all(running);
}
}
run();
}
// 执行并发控制,最多同时执行3个任务
concurrentControl(taskGenerator, 3);
运行后可以看到任务会按照最多3个同时执行的方式依次完成,Generator函数负责提供所有任务的迭代,并发控制逻辑只需要关注执行队列的管理即可,结构非常清晰。
Generator函数使用的注意事项
- Generator函数执行后返回的是迭代器对象,不是函数的执行结果,需要调用next方法才能推进执行。
- yield关键字只能在Generator函数内部使用,在普通函数中使用会报错。
- Generator函数的返回值可以被for...of循环直接遍历,遍历的是yield后面的值,直到遇到done为true为止。
- 如果需要在Generator函数内部捕获错误,可以使用iterator.throw方法抛出错误,在函数内部用try...catch捕获。
Generator函数的这些特性让它在上述场景中具备其他方案难以替代的优势,开发者可以根据实际的业务需求选择合适的场景使用,提升代码的简洁性和可维护性。
Generator函数JavaScript异步编程迭代器状态机修改时间:2026-07-20 00:33:43