JavaScript 生成器:为你的代码提供“暂停”按钮
Source: Dev.to
介绍
如果你了解标准的 JavaScript 函数,你一定知道 run‑to‑completion 规则:一旦普通函数开始执行,在遇到 return 语句或代码块结束之前,任何事情都无法中止它。生成器打破了这条规则。
生成器是可以在执行过程中 暂停 的函数,允许其他代码运行,然后再从暂停的地方继续执行,并且记住所有变量。可以把普通函数想象成必须坐到终点的过山车,而生成器更像 Netflix 连续剧——你可以看一点,暂停去做别的事,然后再从同一位置继续观看。
定义生成器
使用两种特殊语法:
function*– 星号告诉 JavaScript 这是一个生成器。yield– “暂停”按钮。
function* myGenerator() {
console.log("1. Starting...");
yield "First Pause"; // Pauses here and outputs "First Pause"
console.log("2. Resuming...");
yield "Second Pause"; // Pauses here and outputs "Second Pause"
console.log("3. Finishing!");
return "Done";
}
创建并运行生成器
调用生成器函数 不会 执行其代码;它返回一个 生成器对象(迭代器),相当于遥控器。
const gen = myGenerator(); // No console logs happen here!
要开始执行,调用生成器对象的 .next()。每次调用会运行代码直到下一个 yield(或 return),并返回一个对象:
value–yield后面的内容。done– 当生成器还能继续时为false,完成时为true。
console.log(gen.next());
// Output:
// "1. Starting..."
// { value: "First Pause", done: false }
console.log(gen.next());
// Output:
// "2. Resuming..."
// { value: "Second Pause", done: false }
console.log(gen.next());
// Output:
// "3. Finishing!"
// { value: "Done", done: true }
向生成器发送数据
yield 也可以接收数据。当你调用 .next(value) 时,提供的 value 成为暂停的 yield 表达式的结果。
function* chattyGenerator() {
const name = yield "What is your name?";
const age = yield "How old are you?";
return `Hello ${name}, you are ${age}!`;
}
const gen2 = chattyGenerator();
console.log(gen2.next().value); // "What is your name?"
console.log(gen2.next("John").value); // "How old are you?"
console.log(gen2.next(30).value); // "Hello John, you are 30!"
惰性(按需)数据生成
生成器非常适合生成可能无限的序列,而不会耗尽内存。
function* idGenerator() {
let id = 1;
while (true) {
yield id;
id++;
}
}
const ids = idGenerator();
console.log(ids.next().value); // 1
console.log(ids.next().value); // 2
console.log(ids.next().value); // 3
委托给另一个生成器 (yield*)
yield* 将控制权转交给另一个生成器,实现组合。
function* guestDJ() {
yield "Song A";
yield "Song B";
}
function* mainDJ() {
yield "Intro";
yield* guestDJ(); // Hand over control until guestDJ finishes
yield "Outro";
}
for (const song of mainDJ()) {
console.log(song);
}
// Output:
// Intro
// Song A
// Song B
// Outro
生成器与异步代码
在 async/await 出现之前,像 Redux‑Saga 这样的库使用生成器来处理异步流程。如果生成器 yield 一个 Promise,运行器会暂停执行,直到该 promise 解析。
function* fetchUser() {
// Yield the promise; the runner waits for it to resolve.
const user = yield fetch('/api/user');
// The resolved value is passed back into the generator.
console.log(user.name);
}
概念上,function* 类似于 async function,而 yield 的行为类似于 await。
总结 – 生成器速查表
| 语法 | 含义 |
|---|---|
function* | 声明一个生成器函数 |
yield value | 暂停执行并输出 value |
const x = yield | 暂停并等待通过 .next() 传入的数据 |
generator.next() | 运行代码直到下一个 yield(或 return) |
yield* otherGenerator() | 委托给另一个生成器 |
生成器让你能够手动控制代码中的时间流。虽然 async/await 已经在大多数简单的数据获取场景中取代了它们,但生成器在处理复杂流程、无限数据流以及状态机实现时仍然非常强大。