JavaScript中的迭代器(iterator)和生成器(generator)详解

什么是迭代器?

迭代器是一种思想,一种设计模式

什么是迭代?

从一个数据集合中按照一定顺序,不断地取数据的过程

迭代和遍历的区别:

  • 迭代可以不确定取出的值有多少,也可以不取完所有数据,强调的是过程。
  • 遍历必须确定数据的长度,循环不断的全部取出,针对于数据量过大的情况下使用遍历,需要时间过长,强调的是结果。

数组的迭代

let arr = [10, 20, 30]

//创建迭代器
let iter = arr[Symbol.iterator]() //可迭代对象都含有一个Symbol.iterator方法,返回一个迭代器

//迭代器都含有一个next方法
//value就是值,done表示迭代是否结束
console.log(iter.next()) // {value: 10, done: false}
console.log(iter.next()) // {value: 20, done: false}
console.log(iter.next()) // {value: 30, done: false}
console.log(iter.next()) // {value: undefined, done: true}

可迭代对象

可以用for..of遍历的就是可迭代对象,可迭代对象可以创建出迭代器。

例如:数组、字符串、Map、dom元素…

注意:对象不是可迭代对象!!!

设计可迭代对象

我们可以自行把一个对象设计为可迭代对象:

为了让对象可迭代(也就让 for..of 可以运行)我们需要为对象添加一个名为 Symbol.iterator 的方法(一个专门用于使对象可迭代的内建 symbol)。

  1. for..of 循环启动时,它会调用这个方法(如果没找到,就会报错)。这个方法必须返回一个 迭代器(iterator) —— 一个有 next 方法的对象。
  2. 从此开始,for..of 仅适用于这个被返回的对象
  3. for..of 循环希望取得下一个数值,它就调用这个对象的 next() 方法。
  4. next() 方法返回的结果的格式必须是 {done: Boolean, value: any},当 done=true 时,表示循环结束,否则 value 是下一个值。

这是带有注释的完整实现:

let range = {
    
    
    stus: ["小明", "小红", "小刚"],

    //添加一个Symbol.iterator方法
    [Symbol.iterator]() {
    
    
        let index = 0; //对应的索引
        const this_ = this;
        //返回一个迭代器————一个有 next 方法的对象
        return {
    
    
            //next方法
            next() {
    
    
                //如果有元素返回元素值和done:false,否则返回{ value: undefined, done: true }
                if (index < this_.stus.length) {
    
    
                    const ret = {
    
     value: this_.stus[index], done: false };
                    index++;
                    return ret;
                } else {
    
    
                    return {
    
     value: undefined, done: true };
                }
            },
        };
    },
};

for (let num of range) {
    
    
    alert(num); // 1, 然后是 2, 3, 4, 5
}

什么是生成器?

生成器是一种可以用来控制迭代器(iterator)的函数,它可以随时暂停,并可以在任意时候恢复。

生成器函数

写法:

  • function 与函数名之间有一个星号 *
  • 函数体内部有yield关键字
function * genFunc(){
    
    
    yield 1;
    yield 2;
    yield 3;
}

生成器函数的执行机制

genFunc();

这并不是执行函数里面的代码,而是创建了一个迭代器

这是通过生成器来创建一个迭代器

let iter = genFunc();
iter.next();

调用next才开始去执行生成器函数里面的代码,当执行碰到yiekd时就会停下执行,并跳出这个函数,返回yield后面的值(加工成一个对象)给调用next的地方

iter.next();

再次调用next,再次进入到生成器函数里面执行代码,碰到yiekd时又会停下执行,如此反复

总结:

  1. 通过调用生成器函数来创建迭代器(此时生成器函数不会执行)
  2. 通过迭代器调用next执行生成器函数里面的代码
  3. 执行到yield暂时跳出函数并把yiely后面的值加工成对象返回到调用next的地方

应用场景

第一个异步请求成功后再去发送第二个请求,第二个请求成功后再去发送第三个请求,每一个请求是基于前面一次请求成功后才来发送请求,因为要拿到上一次的数据

  • 基于Promise的写法
function useApi(x){
    
    
    return new Promise((resolve, reject) =>{
    
    
        //模拟一秒后得到响应
        setTimeout(() =>{
    
    
            resolve(++x)
        },1000)
    })
}

useApi(0).then(result1 =>{
    
    
    console.log('第一次请求结果为:${result1}');
    return useApi(result1)
}).then(result2 =>{
    
    
    console.log('第二次请求结果为:${result2}');
    return useApi(result2)
}).then(result3 =>{
    
    
    console.log('第三次请求结果为:${result3}');
    return useApi(result3)
})
  • 生成器创建迭代器的写法
function useApi(x){
    
    
    return new Promise((resolve, reject) =>{
    
    
        //模拟一秒后得到响应
        setTimeout(() =>{
    
    
            resolve(++x)
        },1000)
    })
}

function * genFunc(x){
    
    
    let result1 = yield useApi(x)
    console.log('第一次请求结果为:${result1}');
    let result2 = yield useApi(result1)
    console.log('第二次请求结果为:${result2}');
    let result3 = yield useApi(result2)
    console.log('第二次请求结果为:${result3}');
}

本文同步更新到我的博客,欢迎大家前来访问~
https://blog.zlpo.xyz/

猜你喜欢

转载自blog.csdn.net/weixin_57006241/article/details/126187685