Generator函数详解(精简版)

闲言细语: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();

生成器对象

方法

  1. next() 方法: 这个方法用于执行生成器函数的下一步操作,产生一个对象,该对象包含 valuedone 两个属性。value 表示生成器函数的返回值或 yield 表达式的结果,done 是一个布尔值,表示生成器是否已经完成。
  2. 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);
}

结尾

欢迎指正~

相关推荐
程序员爱技术1 小时前
Vue 2 + JavaScript + vue-count-to 集成案例
前端·javascript·vue.js
并不会2 小时前
常见 CSS 选择器用法
前端·css·学习·html·前端开发·css选择器
悦涵仙子2 小时前
CSS中的变量应用——:root,Sass变量,JavaScript中使用Sass变量
javascript·css·sass
衣乌安、2 小时前
【CSS】居中样式
前端·css·css3
兔老大的胡萝卜2 小时前
ppk谈JavaScript,悟透JavaScript,精通CSS高级Web,JavaScript DOM编程艺术,高性能JavaScript pdf
前端·javascript
低代码布道师2 小时前
CSS的三个重点
前端·css
耶啵奶膘3 小时前
uniapp-是否删除
linux·前端·uni-app
王哈哈^_^5 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie5 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic6 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js