JavaScript 生成器函数(generator)详解:优雅处理异步任务流_generator js-程序员宅基地

技术标签: 前端  es6  javascript  ecmascript  

目录

1. 生成器函数的定义和使用

2. 暂停和恢复执行

3. 与其他语言特性的配合使用

Iterator Protocol 迭代器协议 

解构赋值 

生成器和 Promise 的组合使用 

        使用 Promise:

        使用 async/await:

委托给另外一个Generator函数


        Generators 是 JavaScript 中的一种特殊函数,它们可以暂停执行并根据需要生成多个值。通过 yield 关键字,生成器函数可以在每次被调用时产生一个新值,这使得它们非常适合处理大量数据或需要延迟计算的场景。本文将详细介绍 generators 的作用、用法以及与其他语言特性的配合使用。

1. 生成器函数的定义和使用

        生成器函数是通过一个特殊的函数关键字 function* 来定义的。在生成器函数内部,可以使用 yield 关键字来指定需要生成的值。以下是生成器函数的示例:

function* myGenerator() {
    yield 'Apple';
    yield 'Banana';
    yield 'Cherry';
}

const generator = myGenerator();

console.log(generator.next()); // 输出: { value: 'Apple', done: false }
console.log(generator.next()); // 输出: { value: 'Banana ', done: false }
console.log(generator.next()); // 输出: { value: 'Cherry', done: false }
console.log(generator.next()); // 输出: { value: undefined, done: true }

        通过调用生成器函数,我们可以获得一个生成器对象 generator。每次调用 generator.next() 都会返回一个包含 valuedone 属性的对象。

  • value 表示下一个生成的值。
  • done 表示是否还有更多的值需要生成。当所有值都被生成后,done 为 true

2. 暂停和恢复执行

        生成器函数的强大之处在于它们能够暂停和恢复执行,这意味着可以在需要时延迟计算或逐步处理大量数据,而不是一次性全部处理。以下示例展示了如何利用生成器函数处理大型数据集:

function* generateNumbers() {
    for (let i = 0; i <= 1000000; i++) {
        yield i;
    }
}

const numbersGenerator = generateNumbers();

for (let number of numbersGenerator) {
    console.log(number);
}

        在上述示例中,我们定义了一个生成器函数 generateNumbers(),它会生成一个从 0 到 1000000 的数字序列。通过 yield 关键字,每次循环都会产生一个新的数字,并在迭代过程中输出到控制台。通过这种方式,我们可以逐步处理巨大的数据集,避免一次性加载整个数据集导致的性能问题。

3. 与其他语言特性的配合使用

        生成器函数在与其他 JavaScript 特性结合使用时,可以发挥更大的作用。

  • Iterator Protocol 迭代器协议 

        由于生成器函数返回的是一个可迭代对象,因此可以通过 for...of 循环来逐个访问生成的值。

function* shoppingList() {
    yield 'Milk';
    yield 'Eggs';
    yield 'Bread';
}

const myCart = shoppingList();

for (let item of myCart) {
    console.log(item);
}

  • 解构赋值 

        可以通过在生成器函数中使用解构赋值来获取生成的值的特定部分:

function* personDetails() {
    yield ["John", "Doe"];
    yield ["Jane", "Smith"];
}

const fullNameGenerator = personDetails();

for (let [firstName, lastName] of fullNameGenerator) {
    console.log(firstName, lastName);
}

  • 生成器和 Promise 的组合使用 

        生成器函数与异步编程结合使用,可以实现更灵活的控制流,简化异步操作的处理。下面我们分别介绍在生成器函数中如何使用 Promise 和 async/await 来处理异步编程。

        使用 Promise:
function fetchTodos() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(['Todo 1', 'Todo 2', 'Todo 3']);
    }, 2000);
  });
}

function* todoGenerator() {
  yield fetchTodos();
}

let generator = todoGenerator();
let promise = generator.next().value;

promise.then(todos => {
  console.log(todos);  // ['Todo 1', 'Todo 2', 'Todo 3']
});

        在上述代码中,我们定义了一个异步函数 fetchTodos(),它返回一个 Promise 对象,在 2 秒钟后会 resolve 一个包含待办事项的数组。然后,我们定义了一个生成器函数 todoGenerator(),其中通过 yield 关键字将 fetchTodos() 函数作为生成器的值进行暂停。

        在生成器对象上调用 next() 方法后,我们可以获取到 fetchTodos() 返回的 Promise 对象,然后可以使用 .then() 方法处理该 Promise 的结果。

        使用 async/await:
function fetchTodo() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Todo');
    }, 2000);
  });
}

function* todoGenerator() {
  try {
    let result = yield fetchTodo();
    console.log(result); // 'Todo'
    console.log('Generator continues...');

    // 可以在生成器函数中根据需要使用多个异步操作
    let anotherResult = yield someAsyncOperation();
    console.log(anotherResult);
    
    // ...
  } catch (error) {
    console.error(error);
  }
}

async function main() {
  const generator = todoGenerator();

  try {
    while (true) {
      const { value, done } = generator.next();
      
      if (done) {
        break;
      }

      await value;
    }
  } catch (error) {
    console.error(error);
  }
}

main();

        在上面的示例中:

  1. fetchTodo() 函数返回一个 Promise 对象,表示获取待办事项。生成器函数 todoGenerator() 使用 yield 暂停执行,并等待该 Promise 结果。

  2. main() 函数中,我们创建了一个迭代器对象 generator,通过循环并使用 await 关键字来依次执行生成器函数中的异步操作。

  3. 生成器函数中可以根据需要使用多个异步操作,使用 yield 暂停执行并等待每个操作完成。捕获可能的错误,可以使用 try-catch 块。

        PS. 生成器函数本身并不返回 Promise 对象,因此我们需要将生成器函数与 main() 函数结合使用,以确保异步操作按照预期顺序执行。

        总的来说,通过在生成器函数中结合 Promise、async/await 等异步编程特性,可以使生成器函数的控制流更加灵活、简洁和可读,从而提升异步编程的开发体验。

  • 委托给另外一个Generator函数

        委托(delegating)给另一个 Generator 函数是 Generator 函数在使用上的一种常见用法,它允许一个生成器函数调用另一个生成器函数,并将后者的生成器值逐个 yield 出来。这种委托机制可以简化代码结构,提高可读性,同时灵活地处理多个生成器之间的协作关系。

        示例代码:

function* generator1() {
  yield 1;
  yield 2;
}

function* generator2() {
  yield 'a';
  yield 'b';
}

function* combinedGenerator() {
  yield* generator1();  // 委托generator1()
  yield* generator2();  // 委托generator2()
  yield 'Final value';
}

let generator = combinedGenerator();

console.log(generator.next());  // { value: 1, done: false }
console.log(generator.next());  // { value: 2, done: false }
console.log(generator.next());  // { value: 'a', done: false }
console.log(generator.next());  // { value: 'b', done: false }
console.log(generator.next());  // { value: 'Final value', done: false }
console.log(generator.next());  // { value: undefined, done: true }

        在上述代码中,我们定义了三个生成器函数:generator1()generator2()combinedGenerator()。其中,combinedGenerator() 是我们将要创建的委托生成器函数。

        在 combinedGenerator() 中,通过使用 yield* 表达式,我们可以将执行权委托给其他生成器函数,即将 generator1()generator2() 的生成器值依次逐个 yield 出来。这样,在使用 combinedGenerator() 生成的生成器对象上调用 next() 方法时,它会检查当前生成器函数是否有委托的生成器函数可供调用。

        值得注意的是,通过委托给其他生成器函数,不仅可以在合并生成器值时保持代码的模块化和可复用性,还可以处理更复杂的生成器协作场景。在实际开发中,你还可以根据具体需求嵌套多个委托关系,以实现更灵活和高效的生成器编程。

        另外如果在委托生成器函数中发生异常(如:委托的生成器函数中出现错误、被主动生成器函数提前结束),该异常会被传递回主生成器函数并抛出。

        通过委托机制,JavaScript 中的 Generator 函数能够更好地组织和控制生成器之间的协作关系,使得代码更具可读性、可维护性,并且支持构建复杂的生成器流程。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_51588894/article/details/131515619

智能推荐

计算机网络——以太网帧结构/格式_以太网帧包括哪些字段-程序员宅基地

文章浏览阅读5.8k次,点赞4次,收藏15次。目录目的地址源地址类型字段数据字段FCS前同步码格式如图所示:可以看到,以太网帧由6个字段组成:目的地址目标适配器的MAC地址 (即物理地址,也称为链路地址、MAC地址、LAN地址。关于它的详细介绍以及其他寻址方式的介绍可以看这篇博客:计算机网络知识点——寻址(物理地址、逻辑地址、端口地址与专用地址))。源地址传输该帧到局域网上的适配器的MAC地址。类型字段用来标记上一层使用的是什么协议,以便把收到MAC帧的数据上交给上一层的这个协议。数据字段46~1500个字节,46是因为以太网MA_以太网帧包括哪些字段

DWA路径规划算法-程序员宅基地

文章浏览阅读9.7k次,点赞20次,收藏231次。来源丨古月居1.DWA路径规划基本原理动态窗口法主要是在速度(v,w)空间中采样多组速度,并模拟机器人在这些速度下一定时间(sim_period)内的轨迹。在得到多组轨迹以后,对这些轨迹进行评价,选取最优轨迹所对应的速度来驱动机器人运动。该算法突出点在于动态窗口这个名词,它的含义是依据移动机器人的加减速性能限定速度采样空间在一个可行的动态范围内。2.DWA路径规划流程3...._dwa算法

LiveGBS国标视频平台如何获取接入视频通道的直播流地址HLS/HTTP-FLV/WS-FLV/WebRTC/RTMP/RTSP-程序员宅基地

文章浏览阅读3k次。1、背景说明LiveGBS国标GB/T28181流媒体服务器软件,支持设备|平台GB28181注册接入、向上级联第三方国标平台, 可视化的WEB页面管理(页面源码开源);支持云台控制、设备录像检索、回放,支持语音对讲,用户管理, 多种协议流输出,实现浏览器无插件直播。在项目过程中,需要播放视频流。视频集成的方式有几种呢?下面会详细说明2中常见的集成方式。2、视频集成方式分享页面集成直接..._直播流地址

Android 中使用 dlib+opencv 实现动态人脸检测-程序员宅基地

文章浏览阅读230次。1 概述完成 Android 相机预览功能以后,在此基础上我使用 dlib 与 opencv 库做了一个关于人脸检测的 demo。该 demo 在相机预览过程中对人脸进行实时检测,并将检测到的人脸用矩形框描绘出来。具体实现原理如下:采用双层 View,底层的 TextureView 用于预览,程序从 TextureView 中获取预览帧数据,然后调用 dlib 库对帧数据进行..._android com.example.facedetection.face

Oracle不要让临时表空间影响Oracle数据库性能_临时表空间不建会怎样-程序员宅基地

文章浏览阅读1.6k次。不要让临时表空间影响Oracle数据库性能Oracle数据库中进行排序、分组汇总、索引等到作时,会产生很多的临时数据。如有一张员工信息表,数据库中是安装记录建立的时间来保存的。如果用户查询时,使用Order BY排序语句指定按员工编号来排序,那么排序后产生的所有记录就是临时数据。对于这些临时数据,Oracle数据库是如何处理的呢?通常情况下,Oracle数据库会先将这些临时数据存放到内存的_临时表空间不建会怎样

【蓝桥杯选拔赛真题92】Scratch消失的水母 第十五届蓝桥杯scratch图形化编程 少儿编程创意编程选拔赛真题解析-程序员宅基地

文章浏览阅读372次。​scratch消失的水母第十五届青少年蓝桥杯scratch编程选拔赛真题解析一、题目要求编程实现1)每次点击绿旗,水母说“请输入 2~10 的整数",同时在舞台下方显示输入框,如图所示2)输入完成后,在舞台的随机位置,出现输入数量的水母,水母不碰到舞台边缘且大小随机随机范围为 50~150),如图所示,例如:输入的是53)等待1秒后,每只水母一直说自己的大小,如图所示4)然后,当第一次按下空格键时,最大的水母消失,如图所示5)接下来每按下一次空格按键,当前最大的_scratch消失的水母

随便推点

android移动开发项目化教程,腾讯、字节跳动面经已发,专题解析-程序员宅基地

文章浏览阅读123次。前言近日,字节跳动正式启动了2021届秋季校园招聘,为应届毕业生开放超过6000个工作岗位。这一数字超过了该公司往年秋招规模,并与其今年春招规模持平。全年校招人数共计超过1万2千人,远高于同类型互联网公司,体现了字节跳动保持业务快速增长,重视对优秀人才的持续投入。字节跳动校园招聘负责人介绍,该项招聘主要面向2021届毕业生,即2020 年9月至2021年8月期间毕业的大学生群体。这批岗位覆盖字节跳动10多项产品和业务,既包括今日头条、抖音、西瓜视频等旗舰产品,也包括懂车帝、幸福里、番茄小说等垂类应用,以_android移动开发项目化教程

伪分布式hadoop+spark+scala搭建-程序员宅基地

文章浏览阅读465次,点赞17次,收藏5次。看见worker和master就说明spark集群启动成功。切换到spark安装目录的/sbin目录下。使profile文件更新生效。

oracle一个字符,Oracle的一个字符串数据变为多行记录-程序员宅基地

文章浏览阅读391次。Oracle的一个字符串数据变为多行记录--把p_string的值动态切分为表的行数据create or replace type split_rec as object (id varchar2(50),userId varchar2(32767));create or replace type split_tbl as table of split_rec;CREATE OR REPLACE ..._oracle 根据某一个字段的数量生成多行

小甲鱼零基础入门学习python--课后作业(更新至第19讲,持续更新)_小甲鱼零基础入门python课后作业-程序员宅基地

文章浏览阅读2.1w次,点赞30次,收藏268次。本章内容:小甲鱼零基础入门学习python--课后作业1、基础部分的作业2、函数部分的作业3、字典、集合、文件部分作业4、异常5、EasyGui6、类、对象、魔法方法7、模块8、爬虫1、基础部分的作业[课后作业] 第001讲:我和Python的第一次亲密接触 | 课后测试题及答案. [课后作业] 第002讲:用Python设计第一个游戏 | 课后测试题及答案.[课后作业] 第003讲:......_小甲鱼零基础入门python课后作业

mysql5.6启动错误1053_Windows无法启动MySQL服务,错误 1053-程序员宅基地

文章浏览阅读1.4k次。解决方法:正常重启电脑试试看,如果不行,在用下面试试MYSQL 1053错误 解决方法:在DOS命令行使用第一步:运行 -> cmd + 回车 (输入下面的命令)mysqld-nt remove mysqlservice的方式卸载此服务,它提示卸载成功,(是英文成功的意思)注:如果提示失败,服务正在运行,在任务栏中结束mysqld-nt.exe第二步:mysqld-nt install my..._mysqld-nt remove mysqlservice

【RT-Thread学习笔记】一起学习下RT-Thread的C语言编码规范_c语言rt_-程序员宅基地

文章浏览阅读7.2k次,点赞4次,收藏12次。本文主要介绍了RRT开源操作系统的C语言编码规范,大家一起来学习吧。_c语言rt_

推荐文章

热门文章

相关标签