zoukankan      html  css  js  c++  java
  • 迭代器,生成器(generator)和Promise的“微妙”关系

    需要Promise源码版的朋友:传送链接

    本文主要讲述(iterator)和生成器*/yield之间的联系和各自的用法,以及生成器的高配版本aysnc/await的使用。

    大纲:

    • 迭代器(iterator)
    • 生成器 */yield
    • 异步版生成器 aysnc/await

    迭代器(iterator)

    先瞅瞅“迭代”,这个词是什么意思呢?每一次“过程”的重复,称之为迭代。不过迭代是会保留结果的,也就说每次都是以上一次迭代的结果为基准,开始下一次的迭代。举个例子,迭代这个词经常出现在产品开发之中,每个周期都会有产品的迭代开发,但是不可能每次都是从零开始做产品,肯定是基于上一版本的产品进行开发,也就是进行迭代。

    从中我们可以整理出关于迭代的两个关键点:

    • 过程是重复的
    • 返回上一次的迭代结果

    那么JS中的“迭代器”是个怎样的概念呢?

    查看MDN中的概念:传送地址

    个人观点:JS中的迭代器,就是一个数组对象,不断地调用next重复获取过程,然后每次都返回一个结果。等到没有东西可返回了,就终止。因此next的返回对象有两个属性donevaluedone表示是否结束了,value表示当前迭代的结果。当donetrue的时候,表示迭代已结束,这时候是没有返回结果的也就是没有value这个属性。

    然而迭代器是有一系列的规范的:

    查看MDN中的概念:传送地址

    迭代器

    • 关于迭代器,就是我们上面讨论的next方法,返回donevaluedone:true时可以省略)两个参数。
    function iteratorFunc(){
        let arr=[...arguments]
        let nIndex=0
        return {
            next:()=>{
                return nIndex<arr.length?
                {value:arr[nIndex++],done:false}:{done:true}
            }
        }
    }
    let a=iteratorFunc(1,2,3)
    console.log(a.next())//{done:false,value:1}
    console.log(a.next())//{done:false,value:2}
    console.log(a.next())//{done:false,value:3}
    console.log(a.next())//{done:true}
    

    可迭代“对象”

    • 关于可迭代“对象”,我们需要再对象上实现@@iterator方法,也就是[Symbol.iterator],返回一个自定义的迭代方法,以表明这个对象是可以迭代的。有些JS内置的对象就是可迭代的,比如String,Array。

    自带的可迭代事例:

    let str="我是欢乐的迭代器"
    let b=str[Symbol.iterator]()
    console.log(b.next())//{value: "我", done: false}
    console.log(b.next())//{value: "是", done: false}
    console.log(b.next())//{value: "欢", done: false}
    

    有没有很神奇啊!用了这么久的字符串,居然还有这种操作。他的效果等同于上方的自定义迭代方法。那么我们来写个自定义的迭代方法:

    str[Symbol.iterator] = function() {
        return { // this is the iterator object, returning a single element, the string "bye"
          next: function() {
            this._index += 2
            if (this._index<str.length) {
              return { value: str[this._index], done: false };
            } else {
              return { done: true };
            }
          },
          _index:-2
        };
    };
    let c=str[Symbol.iterator]()
    console.log(c.next())//{value: "我", done: false}
    console.log(c.next())//{value: "欢", done: false}
    console.log(c.next())//{value: "的", done: false}
    console.log(c.next())//{value: "代", done: false}
    console.log(c.next())//{done: true}
    

    这里我写的迭代器是返回一个隔一个字符。运行成功yeah

    生成器(generator)

    感觉写迭代器还是很绕呢,于是出现了生成器(generator),专门帮我们生成迭代器的存在。

    function * g(){}
    let it= g()
    console.log(it.next())//{value: undefined, done: true}
    

    看到熟悉的结构没有!{value: undefined, done: true},不过我们没有值。这个时候要向大家推荐*的好基友yield,一个yield对应一个next的值。

    我们改写下上方的字符串的迭代器:

    str[Symbol.iterator]= function * (){
        let index=-2;
        while(index<this.length){
            index += 2
            yield this[index]
        }
    }
    let kk=str[Symbol.iterator]()
    console.log(kk.next())//{value: "我", done: false}
    console.log(kk.next())//{value: "欢", done: false}
    console.log(kk.next())//{value: "的", done: false}
    console.log(kk.next())//{value: "代", done: false}
    

    是不是方便了很多。

    我们带着几个疑问来看看生成器:

    • yield的返回值是啥?
    • 执行顺序?

    实例代码:

    function * gy(){
        console.log("zero")
        let fisrt=yield "first"
        console.log("fisrt",fisrt)
        let second=yield "first"
        console.log("second",second)
    }
    let ity= gy()
    

    第一次执行ity.next(),只打印了zero

    第二次执行ity.next(),只打印了first undefined

    第三次执行ity.next("third"),只打印了second third

    由此可见每次的next都止步于yield,就不再执行下去了。yield每次返回的都是当前ity.next(value)value值。

    aysnc/await

    我们来看看对于Promise这个对象的迭代器,我们该怎么处理。也就是每个迭代器都是异步的。

    function setTime(value,id){
        return new Promise((r,j)=>setTimeout(() => {
            console.log(value)
            r(id)
        }, 10))
    }
    function *a(){
        let r1 = yield setTime("first",1)
        console.log(r1)
        let r2 =yield setTime("second",2)
        console.log(r2)
        let r3 =yield setTime("third",3)
        console.log(r3)
    }
    let k=a();
    new Promise((resolve,reject)=>{
        function next(data){
            let {value,done}=k.next(data)
            //k.next()返回一个promise,因此可以then
            if(!done){
                value.then((data)=>{
                    console.log(data)
                    next(data)
                })
            }
        }
        next();
    })
    

    因为每个都是异步的,所以需要我们二次处理,这个时候aysnc/await就可以出场了。只需要把*/yield无缝改成aysnc/await即可。

    async function a() {
        let r1 = await setTime("first",1)
        console.log(r1)
        let r2 = await setTime("second",2)
        console.log(r2)
        let r3 = await setTime("third",3)
        console.log(r3)
    }
    a()
    
  • 相关阅读:
    小程序云开发(一)
    原来你是这样的"layui"啊
    记个笔记(项目中遇到的关于input的一些操作)
    vue的学习之路 vue-cli与axios
    小程序 上传图片
    文字滚动
    css 隐藏滚动条
    linux基础
    接口测试
    整理python循环,列表,字典笔记
  • 原文地址:https://www.cnblogs.com/cherryvenus/p/9552528.html
Copyright © 2011-2022 走看看