目录
- 迭代器
- next
- 可迭代对象
- 自定义类的迭代
- 迭代器的中断
 
- 生成器
- 生成器函数
- 返回值与传递参数
- 生成器的提前结束
- 生成器的语法糖
- 使用生成器实现自定义类迭代
 
- async和await
- await
 
迭代器
迭代器(iterator),可以把它当做是一个接口,用户可以使用该接口来遍历数据而不需关心数据内部的实现细节
 在JavaScript中,迭代器是一个具体的对象
 这个对象必须含有一个next方法
 拥有迭代器的数据可以被用于for...of,展开运算符,解构赋值,创建对象,调用特定方法等等地方
next
next方法是一个无参数或者只有一个参数的函数,应当返回一个拥有done和value两个属性的对象
 done属性是一个布尔值,它代表了迭代器是否将数据遍历完成,未完成的话值为false,完成或遍历终止的话值为true
 value为迭代器每次在对数据遍历时取得的值,done为true时可以省略,值为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)
}
控制台结果如下

迭代器的中断
在某些情况下遍历可能会中断
 如在遍历的过程中使用了break,return,throw等等
 我们想要监听中断的话可以添加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关键字时,这个函数就变成了异步函数
 异步函数中的代码默认同步执行
 异步函数同样有返回值
- 返回一个普通值
 这个值会被包裹到Promise.resolve()中
- 返回一个Promise
 返回一个Promise则状态由Promise决定
- 返回一个thenable
 返回一个thenable则状态由then方法决定
 如果在异步函数中抛出异常,会作为reject来处理
 在异步函数中最大的特点就是可以使用await关键字
await
await关键字通常会跟一个表达式,表达式返回一个Promise
 await会等待Promise状态变为fulfilled之后再继续执行代码
 await返回的值跟表达式有关
- 如果await后面是一个普通的值,那么会直接返回这个值
- 如果await后面是一个thenable的对象,那么会根据then方法调用来决定后续的值
- 如果await后面的表达式返回的Promise的状态是reject,那么会将这个reject结果直接作为异步函数的Promise的reject值
回到我们最开始的一个问题,我们想要发送一个网络请求并用一个变量接收网络请求的结果
 在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)
            }))
        })
    })
})
而如果使用async和await的话这么写
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()
这就是异步的最终解决方案
















![[C语言]深入浅出,带你构建C语言宏观框架](https://img-blog.csdnimg.cn/7b3b2a8dece74c6ab5131ca5de08712a.png)


