Generator
1. Generator 函数介绍与使用
执行 Generator 函数会返回一个遍历器对象(iterator),可以依次遍历函数内部的每一个状态。
形式上,Generator 函数是一个普通函数,但有两个特征:function* 和 yield,例如:
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
const hw = helloWorldGenerator();
hw.next(); // { value: 'hello', done: false }
hw.next(); // { value: 'world', done: false }
hw.next(); // { value: 'ending', done: true }
hw.next(); // { value: undefined, done: true }
done判断是否存在下一个状态,value对应状态值。yield表达式本身没有返回值,或者说总是返回undefined。- 通过
next方法可以传一个参数,该参数会被当作上一个yield表达式的返回值。
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var b = foo(5);
b.next(); // { value: 6, done: false }
b.next(12); // { value: 8, done: false }
b.next(13); // { value: 42, done: true }
由于 Generator 返回 Iterator,所以可以用 for...of 遍历:
function* foo() {
yield 1; yield 2; yield 3; yield 4; yield 5; return 6;
}
for (let v of foo()) console.log(v); // 1 2 3 4 5
原生对象没有遍历接口,可以通过 Generator 函数为它加上 Iterator 接口:
function* objectEntries(obj) {
let propKeys = Reflect.ownKeys(obj);
for (let propKey of propKeys) yield [propKey, obj[propKey]];
}
for (let [key, value] of objectEntries({ first: 'Jane', last: 'Doe' })) {
console.log(`${key}: ${value}`); // first: Jane / last: Doe
}
2. 异步解决方案
- 回调函数:把第二段任务单独写在回调里,会形成"回调地狱"。
fs.readFile('/etc/fstab', function (err, data) {
if (err) throw err;
console.log(data);
fs.readFile('/etc/shells', function (err, data) { /* ... */ });
});
-
Promise:把嵌套回调改成链式调用,缓解回调地狱,但代码冗杂、语义化不强。
-
Generator:
yield暂停、next恢复,适合把异步任务同步化表达:
const gen = function* () {
const f1 = yield readFile('/etc/fstab');
const f2 = yield readFile('/etc/shells');
};
- async/await:上述 Generator 的语法糖,更简洁、语义更强,是当前异步编程的终极方案。
const asyncReadFile = async function () {
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
};
3. Generator / Promise / async-await 区别
| 维度 | Promise | Generator | async/await |
|---|---|---|---|
| 写法 | .then() 链式 | function* + yield | async + await |
| 自动执行 | 是 | 否(需 co 等执行器) | 是 |
| 错误捕获 | .catch() | try/catch | try/catch |
| 语义化 | 一般 | 较绕 | 最强 |
| 适用 | 简单异步链 | 流式控制 | 几乎所有异步场景 |
4. 使用场景
- 异步解决方案(历史地位,现在已被
async/await取代大部分场景)。 - 给对象加上 Iterator 接口(
for...of)。 - 状态机 / 流式控制(如加载/卸载 UI):
function* loadUI() {
showLoadingScreen();
yield loadUIDataAsynchronously();
hideLoadingScreen();
}
var loader = loadUI();
loader.next(); // 加载
loader.next(); // 卸载
来源整理自:vue3js.cn 面试官系列



