闲言细语:Generator函数是在ES6中引入进来,用来处理异步编程、迭代和流控制等任务。但是,一直都没有完全理解透这究竟是个什么玩意,究竟解决了什么实际的业务场景。带来了什么好处。如果你也和我一样有这个疑惑,那么接着往下看吧。
读音
Generator(gen ne 瑞 ter|/ˈdʒenəreɪtə(r)/)函数。
语法
生成器函数是一种特殊的 JavaScript 函数,生成器必须是function*。
javascript
1、
function* name(){
}
2、
const name = function*(){
}
3、// 箭头函数不能用作generator函数
const faker = *()=>{
}
使用
这个函数不像普通函数一样立刻执行,而是返回一个特殊的对象,我们通常称之为"生成器对象"。(先不管什么是生成器对象,接着看...)每当我调用一次next()方法,它就往下执行一次,直到遇到yield关键字停止。
lua
function* numberGenerator() {
yield 1;
yield 2;
const data = yield 3;
return data;
}
const generator = numberGenerator(); // 返回了一个生成器对象
console.log(generator.next());
// 输出: { value: 1, done: false }
// 返回数据内容浅显解析 value: yield右边的值,done: 代表是否结束了
console.log(generator.next());
// 输出: { value: 2, done: false }
console.log(generator.next());
// 输出: { value: 3, done: false }
console.log(generator.next(9));
//输出: {value: 9, done: true} 为什么这里会返回9呢 因为yield的返回值,是由下一次next传递进去的。所以data=9
console.log(generator.next());
// 输出: { value: undefined, done: true }
// value: 没有内容,所以给了undefined,done: 已经结束了
看完这个示例,大脑中产生了一个想法,所以它有什么用呢?这个时候请看闲言细语第一句话,用来处理异步编程、迭代和流控制等任务。那直接先往异步编程的方向上靠吧。
异步编程
那根据刚刚所学习到的内容,可以写出如下代码:
ini
function* fetchData() {
const data1 = yield fetch('https://test.com/data1'); // yield右边返回的值是一个promise
const data2 = yield fetch('https://test.com/data2');
return [data1, data2];
}
// 使用生成器处理异步数据
const generator = fetchData();
generator.next().value.then(response1 => {
generator.next(response1).value.then(response2 => {
const result = generator.next(response2).value;
console.log(result); // [data1, data2]
});
});
这样写的好处是什么呢?fetchData
生成器函数用于处理两个异步数据请求。通过使用 yield
,你可以逐步获取数据,而不必使用嵌套回调。我觉得还可以再抽象化一点,提取成为一个工具函数。
javascript
function processGenerator(generator) {
const { value, done } = generator.next();
if (done) {
console.log("生成器已完成");
return;
}
if (value instanceof Promise) {
value
.then((response) => {
console.log("Response:", response);
processGenerator(generator); // 递归调用,继续处理下一个值
})
.catch((error) => {
console.error("Error:", error);
});
} else {
console.log("Value:", value);
processGenerator(generator); // 递归调用,继续处理下一个值
}
}
// 处理异步请求的统一方法
const handleAsyncGenerator = (generator) => {
const _generator = generator();
const isGenerator =
_generator &&
typeof _generator.next === "function" &&
_generator.constructor &&
_generator.constructor?.constructor?.name === "GeneratorFunction";
if (!isGenerator) {
return console.error("传入的参数不是Generator函数");
}
processGenerator(_generator);
};
// 放入需要处理的Generator函数
handleAsyncGenerator(fetchData);
有必要这么麻烦吗??? 没必要,这种场景下我直接使用async/await
不就好了,一样可以控制顺序避免嵌套回调了。那么接着往下找,迭代和流控制等任务
。应该就是这个了,开始找业务场景(网上有一堆场景,但我想找一个离业务更近一点的场景,这样应该能够听的懂一些)。
迭代和流控制等任务
有限状态机。(当你的业务流程需要根据状态去做某事时,不妨通过Generator函数去实现,比如:流程审批功能。再使用的时候,只需要执行一句stateMachine.next(),优雅,属实优雅
)。
javascript
function* createFiniteStateMachine() {
let state = 'idle';
while (true) {
switch (state) {
case 'idle':
console.log('当前状态:idle');
yield; // 等待状态转换
state = 'processing';
break;
case 'processing':
console.log('当前状态:processing');
// 在这里执行一些操作
yield; // 等待状态转换
state = 'completed';
break;
case 'completed':
console.log('当前状态:completed');
// 在这里执行一些操作
yield; // 等待状态转换
state = 'idle';
break;
}
}
}
const stateMachine = createFiniteStateMachine();
// 启动状态机
stateMachine.next();
生成器对象
方法
- next() 方法: 这个方法用于执行生成器函数的下一步操作,产生一个对象,该对象包含
value
和done
两个属性。value
表示生成器函数的返回值或yield
表达式的结果,done
是一个布尔值,表示生成器是否已经完成。 - return() 方法: 这个方法用于终止生成器函数的执行,可以提前结束生成器函数的迭代。通常用于清理工作或资源释放。
可迭代性 可以使用 for...of
循环、Array.from()
、...spread
等方式迭代生成器对象的值。
javascript
function* myGenerator() {
yield 1;
yield 2;
yield 3;
}
const generator = myGenerator();
for (const value of generator) {
console.log(value);
}
也可以自定义迭代方法.
迭代器协议要求返回的对象必须包含一个 next()
方法,用于迭代值和判断是否迭代完成
javascript
const myIterable = {
data: [1, 2, 3, 4, 5],
index: 0,
[Symbol.iterator]: function() {
const self = this; // 保存对当前对象的引用
let currentIndex = 0; // 迭代的索引
return {
next: function() {
if (currentIndex < self.data.length) {
return { value: self.data[currentIndex++] * 2, done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
};
// 使用自定义迭代器
for (const value of myIterable) {
console.log(value);
}
结尾
欢迎指正~