js迭代器与生成器

目录

迭代器

迭代器iterator),可以把它当做是一个接口,用户可以使用该接口来遍历数据而不需关心数据内部的实现细节

JavaScript中,迭代器是一个具体的对象

这个对象必须含有一个next方法

拥有迭代器的数据可以被用于for...of展开运算符解构赋值创建对象调用特定方法等等地方

next

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

javascript 复制代码
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访问

javascript 复制代码
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出来的对象都是可迭代对象

javascript 复制代码
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方法

javascript 复制代码
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控制函数执行流程
生成器函数会返回一个生成器对象

javascript 复制代码
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())

控制台结果如下

返回值与传递参数

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

javascript 复制代码
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方法

javascript 复制代码
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外部可以继续函数的执行

生成器的语法糖

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

javascript 复制代码
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())

控制台结果如下

上面这种代码本质上是下面这种代码的语法糖写法

javascript 复制代码
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())

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

javascript 复制代码
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

javascript 复制代码
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()

使用生成器迭代器的写法

javascript 复制代码
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的话这么写

javascript 复制代码
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()

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

相关推荐
GISer_Jing16 分钟前
Vue3状态管理——Pinia
前端·javascript·vue.js
好开心3331 分钟前
axios的使用
开发语言·前端·javascript·前端框架·html
又蓝1 小时前
使用 Python 操作 Excel 表格
开发语言·python·excel
余~~185381628001 小时前
稳定的碰一碰发视频、碰一碰矩阵源码技术开发,支持OEM
开发语言·人工智能·python·音视频
Am心若依旧4092 小时前
[c++11(二)]Lambda表达式和Function包装器及bind函数
开发语言·c++
明月看潮生2 小时前
青少年编程与数学 02-004 Go语言Web编程 20课题、单元测试
开发语言·青少年编程·单元测试·编程与数学·goweb
大G哥2 小时前
java提高正则处理效率
java·开发语言
VBA63372 小时前
VBA技术资料MF243:利用第三方软件复制PDF数据到EXCEL
开发语言
轩辰~2 小时前
网络协议入门
linux·服务器·开发语言·网络·arm开发·c++·网络协议
小_太_阳2 小时前
Scala_【1】概述
开发语言·后端·scala·intellij-idea