在 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
仍然在某些场景下非常有用,尤其是在需要与旧代码或特定库兼容的情况下。