一、引言
在 JavaScript 中,Generator
是一种强大的函数形式,允许函数在执行过程中"暂停"和"恢复"。而在 TypeScript 中,Generator
同样受支持,并且拥有完整的类型推导与定义能力,使得开发者可以更安全、更清晰地使用这一特性。
本篇文章将系统总结 TypeScript 中如何使用 yield
与 Generator,包括语法、类型定义、应用场景和注意事项。
二、什么是 Generator?
Generator 函数是一种可以 中断执行并返回多个值 的函数。它以 function*
的形式声明,通过 yield
表达式逐步"产出"值。
ts
function* simpleGenerator() {
yield 1;
yield 2;
yield 3;
}
调用 simpleGenerator()
并不会立即执行函数体,而是返回一个 Iterator 对象,通过调用其 .next()
方法,逐步获得每一个 yield
的值。
ts
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 }
三、在 TypeScript 中使用 Generator
3.1 基本类型定义
TypeScript 可以对 Generator 的返回值、输入值、return
结果进行严格的类型定义:
ts
function* gen(): Generator<number, string, unknown> {
yield 1;
yield 2;
return "done";
}
上面的泛型参数含义如下:
ts
Generator<YieldType, ReturnType, NextType>
YieldType
: 每次yield
返回的值类型ReturnType
: 函数通过return
最终返回的值类型NextType
: 外部传入.next(value)
的值类型
3.2 接收外部输入
你可以在 Generator 中通过 yield
表达式接收 .next()
传入的值:
ts
function* adder(): Generator<string, number, number> {
const a = yield "Please provide a number";
const b = yield "Another number please";
return a + b;
}
const g = adder();
console.log(g.next()); // { value: "Please provide a number", done: false }
console.log(g.next(10)); // { value: "Another number please", done: false }
console.log(g.next(20)); // { value: 30, done: true }
四、Generator 的应用场景
4.1 惰性计算(Lazy Evaluation)
ts
function* infiniteCounter(): Generator<number> {
let i = 0;
while (true) {
yield i++;
}
}
适用于需要按需计算而不是一次性生成的场景,比如大数据分页、日志流等。
4.2 数据流处理
ts
function* dataStream(data: string[]): Generator<string> {
for (const item of data) {
yield item.toUpperCase();
}
}
可以与 for...of
搭配,实现流式数据处理。
4.3 协程式异步控制(传统用法)
虽然现代 JavaScript 推荐使用 async/await
,但 Generator 曾用于模拟协程控制流(如 co
库):
ts
function* flow() {
const result1 = yield fetch("/api/data1");
const result2 = yield fetch(`/api/data2?id=${result1.id}`);
return result2;
}
五、Generator 与 Iterable 协议
Generator 本身实现了 Iterable
接口,因此可以直接用于 for...of
循环:
ts
function* countdown(n: number) {
while (n > 0) {
yield n--;
}
}
for (const i of countdown(3)) {
console.log(i); // 3, 2, 1
}
也可以使用展开运算符:
ts
console.log([...countdown(3)]); // [3, 2, 1]
六、与异步 Generator 的对比(简要)
TypeScript 同样支持 async function*
来创建异步 Generator:
ts
async function* asyncGen(): AsyncGenerator<number> {
for (let i = 0; i < 3; i++) {
await new Promise(res => setTimeout(res, 100));
yield i;
}
}
(async () => {
for await (const n of asyncGen()) {
console.log(n);
}
})();
区别:
yield
可用于同步 Generator;await yield
可用于异步 Generator。
七、注意事项
yield
不能在普通函数中使用,只能在function*
中使用;- 不同于
return
,yield
不会结束函数; - 每次调用
.next()
会推进函数执行到下一个yield
; Generator
不适合复杂异步逻辑处理(推荐使用async/await
)。
八、总结
功能点 | 是否支持 | 说明 |
---|---|---|
function* |
✅ | 创建一个 Generator 函数 |
yield |
✅ | 用于返回值,并暂停函数执行 |
.next(value) |
✅ | 继续执行函数,并传值进来 |
类型定义 | ✅ | Generator 泛型支持三类类型 |
异步 Generator | ✅ | 支持 async function* |
在 TypeScript 中使用 Generator,可以帮助你实现更加清晰的迭代逻辑和惰性计算模型,尤其在处理数据流、实现中间状态管理等场景时,具有独特的优势。