js迭代器与生成器

news2025/2/9 12:26:05

目录

  • 迭代器
    • next
    • 可迭代对象
    • 自定义类的迭代
    • 迭代器的中断
  • 生成器
    • 生成器函数
    • 返回值与传递参数
    • 生成器的提前结束
    • 生成器的语法糖
    • 使用生成器实现自定义类迭代
  • async和await
    • await

迭代器

迭代器iterator),可以把它当做是一个接口,用户可以使用该接口来遍历数据而不需关心数据内部的实现细节
JavaScript中,迭代器是一个具体的对象
这个对象必须含有一个next方法
拥有迭代器的数据可以被用于for...of展开运算符解构赋值创建对象调用特定方法等等地方

next

next方法是一个无参数或者只有一个参数的函数,应当返回一个拥有done和value两个属性的对象
done属性是一个布尔值,它代表了迭代器是否将数据遍历完成,未完成的话值为false,完成或遍历终止的话值为true
value为迭代器每次在对数据遍历时取得的值donetrue时可以省略,值为undefined 我们可以实现一个数组的迭代器,通过这个迭代器来遍历数组

var arr = [1, 2, 3, 4, 5]
var arrIndex = 0
var arrIterator = {
    next: function () {
        if (arrIndex >= arr.length) {
            return { done: true, value: undefined }
        } else {
            return { done: false, value: `arr第${arrIndex}元素是${arr[arrIndex++]}` }
        }
    }
}
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())

控制台结果如下

结果

可迭代对象

我们知道在JavaScript对象本身是不可迭代
如果一个对象实现了迭代器时那么这个对象就变成了一个可迭代对象
迭代器的名称必须为@@iterator,在代码中我们可以使用Symbol.iterator访问

var obj = {
    arr: [1, 2, 3, 4, 5],
    [Symbol.iterator]() {
        var index = 0
        return {
            next: () => {
                if (index >= this.arr.length) {
                    return { done: true, value: undefined }
                } else {
                    return { done: false, value: this.arr[index++] }
                }
            }
        }
    }
}
var objIterator = obj[Symbol.iterator]()
console.log(objIterator.next())
console.log(objIterator.next())
console.log(objIterator.next())
console.log(objIterator.next())
console.log(objIterator.next())
console.log(objIterator.next())
for (var item of obj) {
    console.log(item)
}

通过对对象实现迭代器的形式成功让对象可以迭代

自定义类的迭代

我们还可以创建一个类,所有通过这个类new出来的对象都是可迭代对象

class Person {
    constructor() {
        this.name = "张三"
        this.age = "18"
    }
    [Symbol.iterator]() {
        var index = 0;
        var items = Object.entries(this)
        return {
            next: () => {
                if (index >= items.length) {
                    return { done: true, value: undefined }
                } else {
                    return { done: false, value: items[index++] }
                }
            }
        }
    }
}
var p1 = new Person()
for (var item of p1) {
    console.log(item)
}

控制台结果如下

结果

迭代器的中断

在某些情况下遍历可能会中断
如在遍历的过程中使用了breakreturnthrow等等
我们想要监听中断的话可以添加return方法

class Person {
    constructor() {
        this.name = "张三"
        this.age = "18"
    }
    [Symbol.iterator]() {
        var index = 0;
        var items = Object.entries(this)
        return {
            next: () => {
                if (index >= items.length) {
                    return { done: true, value: undefined }
                } else {
                    return { done: false, value: items[index++] }
                }
            },
            return: () => {
                console.log("监听到了中断操作")
                return { done: true, value: undefined }
            }
        }
    }
}
var p1 = new Person()
for (var item of p1) {
    console.log(item)
    break
}

控制台结果如下
结果

生成器

生成器Generator)是一种新的函数控制解决方案,它能够更加灵活的控制函数什么时候执行,什么时候暂停
生成器本质上是一个特殊的迭代器

生成器函数

生成器函数是一个特殊的函数,生成器函数需要在Function标识符的后面加一个*
生成器函数能通过yield控制函数执行流程
生成器函数会返回一个生成器对象

function* foo() {
    console.log("aaa")
    yield
    console.log("bbb")
    yield
    console.log("ccc")
    yield
    console.log("ddd")
}
var generator = foo()
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())

控制台结果如下
结果

返回值与传递参数

生成器同样有返回值与传递参数

function* foo(next1) {
    console.log("aaa", next1)
    var next2 = yield "return1"
    console.log("bbb", next2)
    var next3 = yield "return2"
    console.log("ccc", next3)
    var next4 = yield "return3"
    console.log("ddd", next4)
}
var generator = foo("next1")
console.log(generator.next())
console.log(generator.next("next2"))
console.log(generator.next("next3"))
console.log(generator.next("next4"))
console.log(generator.next("next5"))

控制台结果如下

结果
可以看到在next中传入参数时会被放入yield前接收的变量中
值得注意的是第一次调用next时是不用传递参数的,因为没有yield前面的变量接收参数
如果想要在第一次调用next就传递参数的话需要再foo中传递
每个yield后面都是返回值,这些返回值被存放在value
生成器函数执行完毕后默认的返回值undefined

生成器的提前结束

如果希望生成器提前结束的话可以调用return方法和throw方法

function* foo(next1) {
    console.log("aaa", next1)
    var next2 = yield "return1"
    console.log("bbb", next2)
    var next3 = yield "return2"
    console.log("ccc", next3)
    var next4 = yield "return3"
    console.log("ddd", next4)
}
var generator = foo("next1")
console.log(generator.next())
console.log(generator.next("next2"))
console.log(generator.return("next3"))
console.log("-------------------------------")
var generator2 = foo("next1")
console.log(generator2.next())
console.log(generator2.next("next2"))
console.log(generator2.throw(new Error("生成器抛出异常")))

控制台结果如下
结果

return方法调用后这个生成器函数就不会再执行了
throw方法则会向生成器函数内部抛出一个异常
生成器函数内部可以使用try catch捕获异常
如果不捕获异常的话生成器函数会停止运行
使用try catch捕获的话在catch内部无法继续yield,在catch外部可以继续函数的执行

生成器的语法糖

如果我们想用生成器来遍历数组的话可以这么写

var arr = [1, 2, 3, 4, 5]
function* foo(arr) {
    yield* arr
}
var generator = foo(arr)
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())

控制台结果如下
结果
上面这种代码本质上是下面这种代码的语法糖写法

var arr = [1, 2, 3, 4, 5]
function* foo(arr) {
    for (var i = 0; i < arr.length; i++) {
        yield arr[i]
    }
}
var generator = foo(arr)
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())

使用生成器实现自定义类迭代

class Person {
    constructor() {
        this.name = "张三"
        this.age = "18"
    }
    *[Symbol.iterator]() {
        yield* Object.entries(this)
    }
}
var p1 = new Person()
for (var item of p1) {
    console.log(item)
}

控制台结果如下
结果
值得注意的是,如果想在类中实现生成器函数,可以在[Symbol.iterator]前加上一个*

async和await

async用于声明一个异步函数
当一个函数前面添加了async关键字时,这个函数就变成了异步函数
异步函数中的代码默认同步执行
异步函数同样有返回值

  1. 返回一个普通值
    这个值会被包裹到Promise.resolve()
  2. 返回一个Promise
    返回一个Promise则状态由Promise决定
  3. 返回一个thenable
    返回一个thenable则状态由then方法决定
    如果在异步函数抛出异常,会作为reject来处理
    异步函数中最大的特点就是可以使用await关键字

await

await关键字通常会跟一个表达式,表达式返回一个Promise
await会等待Promise状态变为fulfilled之后再继续执行代码
await返回的值跟表达式有关

  1. 如果await后面是一个普通的值,那么会直接返回这个值
  2. 如果await后面是一个thenable的对象,那么会根据then方法调用来决定后续的值
  3. 如果await后面的表达式返回的Promise的状态是reject,那么会将这个reject结果直接作为异步函数Promisereject

回到我们最开始的一个问题,我们想要发送一个网络请求并用一个变量接收网络请求的结果
Promise中是这种写法
具体关于Promise可以看我这篇文章
Promise

function request(url) {
    return new Promise((resolve, reject) => {
        setTimeout(function () {
            resolve("success:" + url)
        }, 1000)
    })
}
function getRequests() {
    var datas = []
    request("http://yes.com").then((res) => {
        datas.push(res)
        return request("http://no.com")
    }).then((res) => {
        datas.push(res)
        return request("http://null.com")
    }).then((res) => {
        datas.push(res)
        return request("http://foo.com")
    }).then((res) => {
        datas.push(res)
    })
    console.log(datas)
}
getRequests()

使用生成器迭代器的写法

function request(url) {
    return new Promise((resolve, reject) => {
        setTimeout(function () {
            resolve("success:" + url)
        }, 1000)
    })
}
function* getRequests() {
    var datas = []
    var temp
    temp = yield request("http://yes.com")
    datas.push(temp)
    temp = yield request("http://no.com")
    datas.push(temp)
    temp = yield request("http://null.com")
    datas.push(temp)
    temp = yield request("http://foo.com")
    datas.push(temp)
    console.log(datas)
}
var generator = getRequests()
generator.next().value.then((res) => {
    generator.next(res).value.then((res) => {
        generator.next(res).value.then((res) => {
            generator.next(res).value.then((res => {
                generator.next(res)
            }))
        })
    })
})

而如果使用asyncawait的话这么写

function request(url) {
    return new Promise((resolve, reject) => {
        setTimeout(function () {
            resolve("success:" + url)
        }, 1000)
    })
}
async function getRequests() {
    var datas = []
    var temp
    temp = await request("http://yes.com")
    datas.push(temp)
    temp = await request("http://no.com")
    datas.push(temp)
    temp = await request("http://null.com")
    datas.push(temp)
    temp = await request("http://foo.com")
    datas.push(temp)
    console.log(datas)
}
getRequests()

这就是异步的最终解决方案

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/864576.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue3 Props组件简单应用(子组件获取父组件数据)

去官网学习→Props | Vue.js 运行示例&#xff1a; 代码&#xff1a;App.vue <template><img alt"Vue logo" src"./assets/logo.png"><!-- 传递数据 key value--><Mycomponent :dataTest"content" :dataNmub&…

GPT-3.5 人工智能还是人工智障?——西红柿炒钢丝球!!

人工智能还是人工智障&#xff1f;——西红柿炒钢丝球 西红柿炒钢丝球的 基本信息西红柿炒钢丝球的 详细制作方法材料步骤 备注幕后花絮。。。。。。。。。关于GPT-3.5&#xff0c;你的看法&#xff1a; 西红柿炒钢丝球的 基本信息 西红柿炒钢丝球是一道具有悠久历史的传统中式…

leetcode经典算法——快速幂

实现 pow(x, n) &#xff0c;即计算 x 的整数 n 次幂函数&#xff08;即&#xff0c;xn &#xff09;。 暴力方法肯定是循环循环n次&#xff0c; 每一次*x 显然此方法遇到大的数字会超时 那么我们要引进一个思想&#xff0c;快速幂算法 例如&#xff1a; x^97 我们可以看出&a…

柜柜软件报价单滑动闪屏解决办法

柜柜下载地址:家具设计软件免费下载-家居设计软件手机版下载-柜柜App官网 出现的问题现象: 原因:笔记本使用的集成显卡,切换到独立显卡即可解决 异常修复.

ONVIF对讲功能漫谈

ONVIF对讲功能漫谈 前言一、onvif对讲功能和onvif协议关系大吗?二、如何上报设备支持onvif对讲功能呢?三、onvif协议中哪个接口上报音频解码配置?四、献上抓包报文:前言 本篇文章尝试使用提问的方式来分享onvif对讲功能那点事。 一、onvif对讲功能和onvif协议关系大吗? on…

windows10中配置mmhuman3d

分类&#xff1a;动作捕捉 github地址&#xff1a;https://github.com/open-mmlab/mmhuman3d 所需环境&#xff1a;Windows10&#xff0c;CUDA11.6&#xff0c;conda 4.13.0&#xff0c;Visual Studio 2017 目录 一.新建Pytorch基本环境1.创建并激活环境2.安装ffmpeg3.安装 PyT…

21、stm32使用LTDC驱动LCD

注&#xff1a;本文基于stm32使用FMC驱动SDRAM(IS42S32800G-6BLI)工程继续开发 本例使用安富莱的H743XIH板子驱动LTDC点亮7寸LCD 硬件接线&#xff1a;RGB888 一、cubemx配置 1、LTDC配置 注意此引脚应于上面的硬件接线图一致 2、配置DMA2D 3、背光引脚和触摸引脚 4、时钟…

cpu使用率,CPU使用率指的是什么

目录 1、cpu使用率CPU使用率指的是什么&#xff1f; 2、cpu利用率100&#xff1f; 3、电脑CPU使用率居高不下&#xff1f; 4、cpu使用率100怎么恢复默认&#xff1f; 5、cpu使用率98&#xff1f; 1、cpu使用率CPU使用率指的是什么&#xff1f; CPU使用率其实就是运行的程…

SQL | 计算字段

7-创建计算字段 7.1-计算字段 存储在数据库中的数据一般不是我们所需要的字段格式&#xff0c; 需要公司名称&#xff0c;同时也需要公司地址&#xff0c;但是这两个数据存储在不同的列中。 省&#xff0c;市&#xff0c;县和邮政编码存储在不同的列中&#xff0c;但是当我们…

B056-Mybatis增强

目录 域对象&#xff08;domain&#xff09;之间的关系A 表结构上是如何设计关系的B 实体对象上是如何设计关系的回顾多表查询 结果映射多对一/一对一DomainDepartmentEmployee MapperEmployeeMapperEmployeeMapper_嵌套结果.xml&#xff08;推荐使用&#xff09;EmployeeMappe…

(4)各个属性角色分析显示-4

将折线图、数据集、散点图集合在一个html文件中&#xff1a; &#xff08;1&#xff09;将折线图、数据集、散点图设置为函数a()、b()、c()&#xff0c; &#xff08;2&#xff09;再调用page.add()函数&#xff0c;将三个图片组合在一起 &#xff08;3&#xff09;运行page.…

python sqlalchemy 动态设置表名__tablename__,一个model对应多个table

我们在上一篇中说明了&#xff0c;如何在.net core的efcore中动态设置表名。 本文讲述如何在sqlalchemy中动态设置表名&#xff0c;使多个table可以对应到一个model 表如下 code example from sqlalchemy import create_engine,Column,BigInteger,String from sqlalchemy.ext…

Java全开源电影票一站式购买平台程序研发

院线电影票H5用户端一站式订购平台设计 随着移动互联网的普及和快速发展&#xff0c;越来越多的用户开始使用手机端进行在线购票。为了满足用户更加便捷、快速地购买电影票&#xff0c;我们设计了一个院线电影票H5用户端一站式订购平台。该平台集成了多种功能&#xff0c;旨在…

腾讯云服务器地域有什么区别怎么选择?

腾讯云服务器地域有什么区别&#xff1f;怎么选择比较好&#xff1f;地域选择就近原则&#xff0c;距离地域越近网络延迟越低&#xff0c;速度越快。关于地域的选择还有很多因素&#xff0c;地域节点选择还要考虑到网络延迟速度方面、内网连接、是否需要备案、不同地域价格因素…

linux系统的压缩、解压详细用法,附代码举例(感觉别人写的都不够好)

文章目录 zipzip支持的选项有&#xff1a;-A 详细解释-d &#xff08;对压缩包操作&#xff09;-e &#xff08;对压缩文件加密&#xff09;-f&#xff08;只更新文件&#xff09;-g&#xff08;不显示压缩过程&#xff09;-r &#xff08;文件夹必选&#xff09;-u&#xff08…

ECRS工时分析:什么叫标准化作业管理?为什么要进行作业标准化管理

中国自古就有标准化。《孙子兵法》中&#xff0c;孙子训练射箭&#xff0c;射箭的姿势是“标准化操作”&#xff1b;中国武术中的套路是“标准化”&#xff1b;在中国古诗中&#xff0c;字数甚至被“标准化”来打开中国历史&#xff0c;“标准化”作业的例子数不胜数。 而在工厂…

[C语言]深入浅出,带你构建C语言宏观框架

导言&#xff1a; 本文章会带你基本了解C语言&#xff0c;对他有一个感性的认识&#xff0c;对其有一个框架&#xff0c;后期在对其进行更加细致的补充。 文章目录 C语言第一个函数变量局部变量 与 全局变量常量字符串 与 转义字符 头文件分支 与 循环注释数组关键字操作符宏指…

【学习FreeRTOS】第4章——FreeRTOS任务创建与删除

1.任务创建和删除的API函数 任务的创建和删除本质就是调用FreeRTOS的API函数 动态方式创建任务——xTaskCreate()静态方式创建任务——xTaskCreateStatic()删除任务——vTaskDelete() 动态创建任务&#xff1a;任务的任务控制块以及任务的栈空间所需的内存&#xff0c;均由 F…

美团研究院:2022年剧本娱乐行业发展报告(附下载)

关于报告的所有内容&#xff0c;公众【营销人星球】获取下载查看 核心观点 户外赛道本质上迎合了全球共性需求的增长&#xff0c;在全球普遍进入大规模城市化、市场经 随着居民收入水乎提高&#xff0c;中国消费者日益注重精神文化生活&#xff0c;文化娱乐成为人们重要的体闲…

Android的学习系列之Android Studio Setup安装

Android的学习系列之Android Studio Setup安装 [TOC](Android的学习系列之Android Studio Setup安装) 前言Android平台搭建总结 前言 还是项目需要&#xff0c;暂时搭建安卓的运行平台。 Android平台搭建 安装包 双击安装包&#xff0c;进入安装。 下一步 根据自己需求&a…