9. 迭代器 Interator() & 生成器 Generator()

357 阅读4分钟

迭代器 Interator()

Interator() 是es6引入的新的遍历机制,有两个核心:

  • 是统一的接口,能快速访问数据,通过 Symbol.interator函数来创建迭代器,通过迭代器的 next() 方法获取每次迭代的结果
  • 迭代器是用于遍历数据结构的指针(数据库的游标),每次 next() 迭代指针会向后移,直到返回 true 表示迭代结束
const items = ['one', 'tow', 'three'];
const ite = items[Symbol.iterator]();
console.log(ite);//Array Iterator {}

console.log(ite.next());//{value: "one", done: false},false表示遍历可以继续
console.log(ite.next());//{value: "tow", done: false}
console.log(ite.next());//{value: "three", done: false}
console.log(ite.next());//{value: undefined, done: false},true表示遍历结束并返回undefined

生成器 Generator()

Generator() 函数可以通过 yield 关键字将函数挂起,为改变函数的执行流提供了可能性,同时为异步编程提供了方案

当声明对象并调用生成器函数后,对象变为一个遍历对象,生成器函数并不执行,而是挂起 fn {<suspended>}

对遍历对象使用 next() 方法,会开始执行函数,直到 yield 所在行,返回 yield 后的变量的值 (或执行yield后的函数) 和状态false,并挂起生成器,

如果后面不再有 yield 会继续执行完生成器函数并返回return结果和状态true,没有return则返回undefined

function* fn() {
    let a = 1, b = 2, c = 3;

    console.log('frist');
    yield a;
    console.log('second');
    yield b;
    console.log('third');
    yield c;
    console.log('end');
}
let fnObj = fn(); 
console.log(fnObj);//fn {<suspended>} 声明生成器变量,并返回函数状态 - 暂停
console.log(fnObj.next());// frist {value: 1, done: false}
console.log(fnObj);//fn {<suspended>}
console.log(fnObj.next());// second {value: 2, done: false}
console.log(fnObj.next());// third {value: 3, done: false}
console.log(fnObj.next());// end {value: undefined, done: true}
console.log(fnObj);//fn {<closed>} 执行完毕后的生成器对象,返回函数状态 - 关闭

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

  • function后面,函数名之前有个 * 表示这是一个生成器函数
  • 只能在函数内部使用 yield 表达式让函数挂起

总结:Generator() 函数是分段执行的,yield 语句让函数暂停执行,next() 方法让函数恢复执行

next()传参

next() 传入的参数,会向上一个 yield 声明的变量赋值 (所以第一个 next 会忽略第一个传参)

function* fn() {
    let a = 1, b = 2, c = 3;

    console.log('frist');
    let x = yield a;
    console.log('second');
    let y = yield b + x;
    console.log('third');
    let z = yield c + y;
    console.log('end');
    return x + y + z + a + b + c;
}
let fnObj = fn();
console.log(fnObj);//fn {<suspended>}
console.log(fnObj.next());// frist {value: 1, done: false}
console.log(fnObj.next(10));// second {value: 12, done: false}
console.log(fnObj.next(20));// third {value: 23, done: false}
console.log(fnObj.next(30));// end {value: 66, done: true}

Generator() 为不具备Interator接口的对象提供了遍历操作

const obj = {
    id: 1,
    name: 'Max',
    age: 23,
}

obj[Symbol.iterator] = objectEntries;//***通过Symbol.iterator让对象具有迭代器的接口,可以进行遍历操作
console.log(obj);//{id: 1, name: "Max", age: 23, Symbol(Symbol.iterator): ƒ}

function* objectEntries(obj) {

    const propKeys = Object.keys(obj);//获取对象的所有key保存到数组[name,age]
    for (const propKey of propKeys) {
        yield [propKey, obj[propKey]]//for循环[键,对象[键] = 值]
    }
}

for (let [key, value] of objectEntries(obj)) {//调用生成器函数并传出obj对象,for循环遍历出键和值
    console.log(`${key}:${value}`);
}

应用: Generator()部署ajax异步操作,让异步代码同步化

//加载loading...
//数据加载完成(异步操作)
//隐藏loading...

function loadUI() {
    console.log('加载loading...动画');
}
function showData() {
    setTimeout(() => { //用setTimeout模拟异步操作
        console.log('加载完成');
        itLoad.next();//4-数据返回之后调用next()继续执行生成器下一步
    }, 1000);
}
function hideUI() {
    console.log('隐藏loading...动画');
}

//生成器函数,让异步操作同步化
function* load() {
    loadUI();
    yield showData();//3-运行到这里挂起并调用showData()函数
    hideUI();//5-隐藏loading...
}

let itLoad = load();//1-调用生成器函数声明迭代器对象,生成器函数被挂起
itLoad.next();//2-首次运行生成器函数

ajax 调用

//声明需要进行同步操作的生成器函数
function* main() {
    console.log('开始运行');
    let res = yield request('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976');
    //3-遇到yield语句,挂起并执行request函数,传入api接口
    console.log(res);
    //后面的操作
    console.log('数据请求完成,可以继续操作');
}
//声明ajax调用函数
function request(url) { //4-接收api接口地址
    $.ajax({
        url,//5-api地址(url:url,接收传入的api参数)
        method: 'get',//6-get方法调用
        success(res) {
            ite.next(res);//把res的值返回给生成器,让生成器继续执行,打印api的返回值
        }
    })
}
const ite = main();//1-调用生成器函数声明迭代器对象,生成器函数被挂起
ite.next();//2-首次执行生成器函数