在 Node.js 中,generator 是一种特殊的函数,它允许你通过 yield 关键字暂停和恢复函数的执行。generator 函数使用 function* 语法来定义,并且返回一个 Generator 对象。这个 Generator 对象可以通过 next() 方法来控制函数的执行流程。
基础
基本用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
function* myGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = myGenerator();
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 }
// 也可以使用 for...of 循环迭代 generator 对象
// for (let res of gen) {
// console.log(res);
// }
// 1
// 2
// 3
|
如上所示,关键点有:
function* 语法: 定义一个 generator 函数。
yield 关键字: 暂停函数的执行,并返回一个值。
next() 方法: 恢复函数的执行,并返回一个对象,包含 value 和 done 两个属性。
value: 当前 yield 返回的值。
done: 表示 generator 函数是否已经执行完毕。
当执行到 done 为 true 时,这个generator对象就已经全部执行完毕,不要再继续调用 next() 了。我们还可以直接用 for...of 循环迭代 generator 对象,这种方式不需要我们自己判断 done 了。
传递参数 🌟
你可以通过 next() 方法向 generator 函数传递参数,这些参数会被当作上一次 yield 表达式的返回值。
1
2
3
4
5
6
7
8
9
10
11
|
function* myGenerator() {
const x = yield 1;
console.log(x); // 输出: 42
yield x;
}
const gen = myGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next(42)); // { value: 42, done: false }
console.log(gen.next()); // { value: undefined, done: true }
|
错误处理
generator 函数可以通过 try...catch 来捕获错误。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
function* myGenerator() {
try {
yield 1;
yield 2;
} catch (e) {
console.log('Caught error:', e);
}
}
const gen = myGenerator();
console.log(gen.next()); // { value: 1, done: false }
gen.throw(new Error('Something went wrong')); // 输出: Caught error: Error: Something went wrong
console.log(gen.next()); // { value: undefined, done: true }
|
与异步操作结合
generator 函数可以与异步操作结合使用,通常与 co 库或 async/await 一起使用。虽然 async/await 是更现代的解决方案,但在某些情况下,generator 仍然有用。
1
2
3
4
5
6
7
8
9
10
11
|
const co = require('co');
function* myGenerator() {
const result1 = yield Promise.resolve(1);
const result2 = yield Promise.resolve(2);
return result1 + result2;
}
co(myGenerator).then(result => {
console.log(result); // 输出: 3
});
|
总结
generator 是一种强大的工具,允许你以一种更灵活的方式控制函数的执行流程。虽然 async/await 已经成为处理异步操作的主流方式,但 generator 仍然在某些场景下非常有用,尤其是在需要手动控制执行流程的情况下。
附录
CO 库是什么
co 是一个基于 generator 的异步流程控制库,由 TJ Holowaychuk 开发。它允许你使用 generator 函数来编写看起来像同步代码的异步代码,从而简化异步编程。co 的核心思想是将 generator 函数与 Promise 结合,自动处理异步操作的执行流程。
你可以通过 npm 安装 co:
基本用法
co 接受一个 generator 函数作为参数,并返回一个 Promise。co 会自动执行 generator 函数中的 yield 表达式,并将其结果包装为 Promise。
1
2
3
4
5
6
7
8
9
10
11
|
const co = require('co');
co(function* () {
const result1 = yield Promise.resolve(1);
const result2 = yield Promise.resolve(2);
return result1 + result2;
}).then(result => {
console.log(result); // 输出: 3
}).catch(err => {
console.error(err);
});
|
支持的 yield 类型
co 支持多种类型的 yield 表达式:
Promise: co 会等待 Promise 完成,并返回其结果。
Thunk: 一个返回单个参数函数的函数(类似于回调函数)。
Array: 并行执行多个 Promise,返回一个包含所有结果的数组。
Object: 并行执行多个 Promise,返回一个包含所有结果的对象。
示例:并行执行
1
2
3
4
5
6
7
8
9
10
11
|
co(function* () {
const [result1, result2] = yield [
Promise.resolve(1),
Promise.resolve(2)
];
return result1 + result2;
}).then(result => {
console.log(result); // 输出: 3
}).catch(err => {
console.error(err);
});
|
示例:对象并行执行
1
2
3
4
5
6
7
8
9
10
11
|
co(function* () {
const results = yield {
a: Promise.resolve(1),
b: Promise.resolve(2)
};
return results.a + results.b;
}).then(result => {
console.log(result); // 输出: 3
}).catch(err => {
console.error(err);
});
|
错误处理
co 会自动捕获 generator 函数中的错误,并将其传递给 Promise 的 catch 方法。
1
2
3
4
5
6
7
8
|
co(function* () {
const result = yield Promise.reject(new Error('Something went wrong'));
return result;
}).then(result => {
console.log(result);
}).catch(err => {
console.error(err.message); // 输出: Something went wrong
});
|
与 async/await 的对比
co 的功能与 async/await 非常相似,但 async/await 是原生支持的语法,而 co 是基于 generator 的库。以下是两者的一个简单对比:
co 示例
1
2
3
4
5
6
7
8
9
10
11
|
const co = require('co');
co(function* () {
const result1 = yield Promise.resolve(1);
const result2 = yield Promise.resolve(2);
return result1 + result2;
}).then(result => {
console.log(result); // 输出: 3
}).catch(err => {
console.error(err);
});
|
async/await 示例
1
2
3
4
5
6
7
8
9
10
11
|
async function myAsyncFunction() {
const result1 = await Promise.resolve(1);
const result2 = await Promise.resolve(2);
return result1 + result2;
}
myAsyncFunction().then(result => {
console.log(result); // 输出: 3
}).catch(err => {
console.error(err);
});
|
总结
co 是一个非常强大的库,它通过 generator 和 Promise 的结合,简化了异步编程的复杂性。虽然 async/await 已经成为现代 JavaScript 中处理异步操作的主流方式,但 co 仍然在某些场景下非常有用,尤其是在需要与旧代码或特定库兼容的情况下。