ES6---(2)迭代器和生成器

x33g5p2x  于2021-09-24 转载在 其他  
字(3.5k)|赞(0)|评价(0)|浏览(427)

一、迭代器 iterator

迭代器是ES6提供的一种接口,为不用的数据结构提供统一的访问机制。

  1. 迭代器是一个接口,能快捷的访问数据,通过Symbol.iterator来创建迭代器 通过迭代器的next()获取迭代后的结果
  2. 迭代器是用于遍历数据结构的指针

用法:

const items = ['a', 'b', 'c'];
let ite = items[Symbol.iterator]();
console.log(ite.next()); // done: false(遍历是否完成) value: "a"

迭代器每次使用next()方法之后都会返回一个IteratorResult的对象:

{done: false, value: ‘a’}

  • done表示迭代是否完成
  • value表示当前值,迭代完成之后为undefined

二、生成器 Generator

Generator 函数 可以通过yield将函数挂起,为了改变执行流提供了可能,同时为了做异步编程提供了方案。
调用生成器函数会产生一个生成器对象,生成器对象实现iteratorable接口,因此具有next方法。

yield关键字可以让生成器停止和开始执行,生成器函数在遇到yield之前正常执行,遇到yield之后,执行停止,函数作用域保留,函数在生成器对象上调用next()方法恢复执行.需要注意的是yield只能在生成器函数内使用

1、用法

生成器函数与普通函数的区别:

  1. 在function和函数名之间有一个/*
  2. 只能在函数内部使用yield表达式,让函数挂起
function* fn() {
    console.log("start");
    yield 2;
    console.log("start2");
    yield 3;
    console.log("end");
    }
let res = fn();

遍历fn()里面的内容:

let res = fn();
console.log(res.next()); // 返回一个遍历器对象
console.log(res.next());
console.log(res.next());

执行结果:

第一次调用next,输出"start",返回{value:2, done:false}
第二次调用next,输出"start2",返回{value:3, done:false}
第三次调用next,执行结束,输出"start3",返回{value:undefined, done:true}

下面我们讲解一个例子加深一些对生成器函数的理解:

function* add() {
    console.log("start");
    // x 不是yield ‘2’ 的返回值,它是next()调用 恢复当前yield()执行传入的实参
    let x = yield '2';
    console.log('one' + x);
    let y = yield '3';
    console.log('two' + y);
    return x + y;
    }
const f = add();
console.log(f.next());
console.log(f.next(20));
console.log(f.next(30));

先创建一个实例对象。
第一次调用next,输出start,执行到给x赋值时暂停,此时并没有赋值。
第二次调用next,传递参数20给x,x此时为20;同时到给y赋值时暂停。
第三次调用next,传递参数30给y,y此时为30,同时返回x+y,执行完毕。

2、生成器的使用场景及应用

使用场景

我们介绍一个生成器的使用场景:为不具备Interator接口的对象提供了遍历操作。我们知道对象内部是没有自带遍历方法的,利用生成器每次返回对象的键值,循环获取。

function* objectEntries(obj) {
    // 将获取对象的所有的key保存在数组[name, age]中
    const propKeys = Object.keys(obj);
    for (const propkey of propKeys) {
        yield [propkey, obj[propkey]];
    }
};
const obj = {
    name: "andy",
    age: 18
};

obj[Symbol.iterator] = objectEntries;
console.log(obj);

for (let [key, value] of objectEntries(obj)) {
    console.log(`${key}: ${value}`);
}
应用-1避免回调地狱

我们知道在实际开发环境中,当请求失败的时候可能会一直请求,造成回调地狱。我们可以借助生成器函数避免回调地狱,每次请求结束之后先yield暂停,直到有需求之后在进行操作,这样就能避免一只调用造成回调地狱。

// 回调地狱
        // $.ajax({
        // url: "https://api.vvhan.com/api/weather?city=" + "北京" + "&type=week",
        // method: 'get',
        // success(res) {
        // // console.log(res);

        // // 继续发送请求
        // $.ajax({
        // url: "https://api.vvhan.com/api/weather?city=" + "北京" + "&type=week",
        // method: 'get',
        // success(res1) {
        // // 继续发送请求
        // $.ajax({
        // url: "https://api.vvhan.com/api/weather?city=" + "北京" + "&type=week",
        // method: 'get',
        // success(res2) {
        // // ...
        // }
        // })
        // }
        // })

        // }
        // })

        function* main() {
            let res = yield request("https://api.vvhan.com/api/weather?city=北京&type=week");
            console.log(res);

            // 执行后面的操作
            console.log("数据请求成功");
        }
        const ite = main();
        ite.next();

        function request(url) {
            $.ajax({
                url: url,
                method: 'get',
                success(res) {
                    ite.next(res)
                }
            })
        }
应用2-同步加载

我们来模拟一个场景:

  1. 页面打开
  2. 数据加载中,需要时间长,设置settimeout模拟(异步)
  3. 关闭加载页面
function loadUI() {
            console.log("打开...");
        }

        function showData() {
            // 模拟异步操作
            setTimeout(() => {
                console.log("数据加载完成...");
                itLoad.next();
            }, 1000);
        }

        function hideUI() {
            console.log("隐藏loading页面");
        }

我们希望能按顺序输出,但是由于js的异步输出,showData()会在执行队列先排队,直到其他两个函数先执行完在执行本身。我们希望可以达到一个同步输出的效果,可以使用生成器函数,在showData()前面加一个yield,这样调用next()之后就可以返回showData()执行并且暂停执行,直至遇到下一个next()执行接下来的语句。

// 加载loading...
        // 数据加载完成...(异步)
        // 关闭loading
        function* load() {
            loadUI();
            yield showData();
            hideUI();
        }
        let itLoad = load(); // 创建迭代器
        itLoad.next()

        function loadUI() {
            console.log("加载中...");
        }

        function showData() {
            // 模拟异步操作
            setTimeout(() => {
                console.log("数据加载完成...");
                itLoad.next();
            }, 1000);
        }

        function hideUI() {
            console.log("隐藏loading页面");
        }

这样就可以实现同步功能了。

下一篇:ES6—(3)Promise对象

相关文章