TypeScript Generator
最后修改时间:2025年3月3日
TypeScript 中的 Generator 是一种特殊的函数,它们按需生成值序列。它们使用 function*
语法和 yield
关键字来暂停和恢复执行。本教程将通过实际示例探讨 Generator,帮助您掌握它们的用法。
基本 Generator 语法
Generator 使用 function*
定义。yield
关键字会暂停执行并返回一个值。此示例演示了一个简单的 Generator。
function* simpleGenerator() { yield 1; yield 2; yield 3; } const generator = simpleGenerator(); console.log(generator.next().value); // Output: 1 console.log(generator.next().value); // Output: 2 console.log(generator.next().value); // Output: 3
在此示例中,simpleGenerator
使用 function*
语法定义,将其标记为 Generator。每次在 Generator 对象上调用 next
时,yield
关键字都会暂停执行并返回一个值。第一次调用 next
会执行到 yield 1
,返回 1;后续调用会从上次暂停点恢复,依次产生 2 和 3。
在最后一个 yield
之后,进一步调用 next
将返回 { value: undefined, done: true }
,表示 Generator 已耗尽。这展示了 Generator 的核心机制:惰性地、顺序地生成值。
无限序列
Generator 可以生成无限序列。此示例生成一个无限的偶数序列。
function* evenNumbers() { let num = 0; while (true) { yield num; num += 2; } } const evens = evenNumbers(); console.log(evens.next().value); // Output: 0 console.log(evens.next().value); // Output: 2 console.log(evens.next().value); // Output: 4
evenNumbers
Generator 使用 while (true)
循环来创建无限的偶数序列,从 0 开始,每次 yield
递增 2。与一次计算所有值的传统函数不同,此 Generator 按需生成值:每次调用 next
都会恢复循环,产生当前的 num
,然后再次暂停。输出显示了 0、2 和 4,但该序列可以无限延续。这突显了 Generator 在不消耗无限内存的情况下处理潜在无限数据以及实现惰性求值的能力。
向 Generator 传递值
Generator 可以通过 next
接收值。此示例演示了向 Generator 传递值。
function* valueReceiver() { const name = yield "What is your name?"; const age = yield `Hello, ${name}! How old are you?`; return `You are ${age} years old.`; } const receiver = valueReceiver(); console.log(receiver.next().value); // Output: What is your name? console.log(receiver.next("Alice").value); // Output: Hello, Alice! How old are you? console.log(receiver.next(30).value); // Output: You are 30 years old.
valueReceiver
Generator 展示了双向通信:它产生提示,并通过 next
接收值。第一次调用 next
会产生“What is your name?”并暂停。第二次调用 next("Alice")
会将“Alice”传递给 yield
表达式,将其赋值给 name
,然后使用该值产生下一个提示。最后,next(30)
将 30 赋值给 age
,并返回一个最终字符串,结束 Generator。输出反映了这种交互流程,展示了 Generator 如何充当有状态的、会话式的迭代器,每次调用 next
都会根据外部输入推进内部逻辑。
组合 Generator
Generator 可以使用 yield*
委托给其他 Generator。此示例组合了两个 Generator。
function* firstGenerator() { yield 1; yield 2; } function* secondGenerator() { yield* firstGenerator(); yield 3; } const combined = secondGenerator(); console.log(combined.next().value); // Output: 1 console.log(combined.next().value); // Output: 2 console.log(combined.next().value); // Output: 3
在此,secondGenerator
使用 yield*
委托给 firstGenerator
,有效地将其序列(1、2)嵌入到自己的序列中。当在 combined
上调用 next
时,它首先按顺序产生 firstGenerator
的值(1,然后是 2),然后继续产生其自身体内的 3。yield*
语法将控制无缝地转移到被委托的 Generator,直到其耗尽,然后恢复父 Generator 的执行。输出(1、2、3)显示了这种可组合性如何允许 Generator 从更简单的 Generator 构建复杂的序列,从而增强了模块化和可重用性。
错误处理
Generator 可以使用 throw
处理错误。此示例展示了 Generator 中的错误处理。
function* errorGenerator() { try { yield 1; yield 2; } catch (error) { yield `Error: ${error}`; } } const errorGen = errorGenerator(); console.log(errorGen.next().value); // Output: 1 console.log(errorGen.throw("Something went wrong").value); // Output: Error: Something went wrong
errorGenerator
将其 yield
包装在 try-catch
块中以管理错误。第一个 next
正常产生 1。然后,throw("Something went wrong")
在最后一个 yield
的位置注入一个错误,该错误被 catch
块捕获,产生一个格式化的错误消息而不是崩溃。
后续调用 next
将表示 Generator 已完成。此示例说明了 throw
如何实现外部错误信号,以及 try-catch
如何确保健壮的错误处理,使 Generator 对中断具有弹性,同时保持其迭代流程。
从 Generator 返回
Generator 可以使用 return
返回最终值。此示例演示了返回值。
function* returnGenerator() { yield 1; yield 2; return "Done"; } const returnGen = returnGenerator(); console.log(returnGen.next().value); // Output: 1 console.log(returnGen.next().value); // Output: 2 console.log(returnGen.next().value); // Output: Done
在 returnGenerator
中,return "Done"
语句提供了一个最终值并终止了 Generator。前两次 next
调用通过 yield
产生 1 和 2,而第三次调用到达 return
,内部产生“Done”并返回 { value: "Done", done: true }
(尽管此处仅记录了值)。
与暂停的 yield
不同,return
会结束 Generator,发出完成信号。此示例展示了如何使用 return
以有意义的结果结束序列,提供一个与惰性产生过程不同的清晰终点。
异步 Generator
异步 Generator 将 Generator 与异步操作结合起来。此示例展示了一个异步 Generator。
async function* asyncGenerator() { yield await Promise.resolve(1); yield await Promise.resolve(2); yield await Promise.resolve(3); } (async () => { const asyncGen = asyncGenerator(); console.log(await asyncGen.next()); // Output: { value: 1, done: false } console.log(await asyncGen.next()); // Output: { value: 2, done: false } console.log(await asyncGen.next()); // Output: { value: 3, done: false } })();
asyncGenerator
使用 async function*
定义一个异步 Generator,将 yield
与 await
结合起来处理来自 Promise.resolve
的异步值。每次 yield
都会等待 Promise 解析,然后暂停并返回该值(1、2、3)。
带有 await
on next
的 IIFE(立即调用函数表达式)会检索每个结果,并记录完整的迭代器对象({ value, done }
)。输出显示了一系列已解析的值,done: false
,并且第四次调用将返回 { value: undefined, done: true }
。这表明异步 Generator 如何惰性地管理异步数据流,非常适合增量获取数据等任务。
带类型注解的 Generator
TypeScript 允许为 Generator 添加类型注解以提高类型安全性。此示例展示了一个类型化的 Generator。
function* numberGenerator(): Generator{ yield 1; yield 2; return "Finished"; } const numGen = numberGenerator(); console.log(numGen.next().value); // Output: 1 console.log(numGen.next().value); // Output: 2 console.log(numGen.next().value); // Output: Finished
numberGenerator
使用 Generator
类型注解,指定 number
作为产生的值的类型,string
作为返回值的类型,以及 void
作为 yield
接收的值的类型(此处为空)。这确保 TypeScript 强制执行 yield
产生数字,return
产生字符串,并在编译时捕获类型不匹配。next
调用产生 1 和 2,然后返回“Finished”,与注解匹配。输出确认了序列,显示了类型注解如何通过静态类型检查增强 Generator,从而提高代码的可靠性和可维护性。
惰性斐波那契序列
Generator 非常适合对 Fibonacci 数等序列进行惰性求值。此示例按需生成 Fibonacci 数。
function* fibonacci() { let a = 0, b = 1; while (true) { yield a; [a, b] = [b, a + b]; } } const fib = fibonacci(); console.log(fib.next().value); // Output: 0 console.log(fib.next().value); // Output: 1 console.log(fib.next().value); // Output: 1 console.log(fib.next().value); // Output: 2
fibonacci
Generator 惰性地创建了一个无限的 Fibonacci 序列。它将 a
和 b
初始化为 0 和 1,每次产生 a
,并使用数组解构更新对以计算下一个数字(b
,然后是 a + b
)。每次调用 next
都会产生下一个 Fibonacci 数——0、1、1、2,依此类推——而无需预先计算整个序列。输出显示了前四个值,展示了 Generator 如何仅在需要时计算值来高效地处理复杂序列,从而节省内存并支持无限序列。
带提前终止的 Generator
Generator 可以使用 return
提前终止。此示例演示了受控终止。
function* countDown(n: number) { while (n > 0) { yield n; n--; } } const counter = countDown(3); console.log(counter.next().value); // Output: 3 console.log(counter.return(0).value); // Output: 0 console.log(counter.next().value); // Output: undefined
countDown
Generator 从 n
递减到 1,但 return(0)
允许提前终止。第一个 next
产生 3,然后 return(0)
停止 Generator,返回 0 并内部返回 { value: 0, done: true }
。后续调用 next
会产生 undefined
,因为 Generator 已完成。这与内部 return
语句不同,因为 return
是一种外部控制机制。输出显示了此方法如何提供灵活性以提前停止 Generator,这对于需要动态控制迭代的场景很有用。
最佳实践
- 利用 Generator 实现惰性序列: 利用 Generator 按需生成值,特别是对于大型或无限序列,以优化内存使用。
- 实现健壮的错误处理: 在 Generator 中使用
try-catch
块来优雅地处理由throw
或内部逻辑故障触发的错误。 - 使用 Yield* 进行组合: 使用
yield*
委托给其他 Generator,以促进复杂序列生成中的模块化和可重用性。 - 为异步数据使用异步 Generator: 应用异步 Generator 来管理异步操作,例如 API 调用或文件读取,在它们解析时产生值。
- 控制无限循环: 使用明确的终止条件(例如通过
return
)或限制来设计无限 Generator,以防止意外的无限迭代。 - 添加类型注解: 包括 TypeScript 类型注解(例如
Generator
)以确保类型安全并提高代码清晰度。 - 测试 Generator 状态: 验证 Generator 在所有状态下的行为——产生、返回和完成——特别是当使用
next
、throw
或return
时。
来源
本教程通过实际示例涵盖了 TypeScript Generator。使用这些模式来创建高效且可维护的代码。