JavaScript及ES6|ES6---(2)迭代器和生成器
一、迭代器 iterator 迭代器是ES6提供的一种接口,为不用的数据结构提供统一的访问机制。
- 迭代器是一个接口,能快捷的访问数据,通过Symbol.iterator来创建迭代器 通过迭代器的next()获取迭代后的结果
- 迭代器是用于遍历数据结构的指针
const items = ['a', 'b', 'c'];
let ite = items[Symbol.iterator]();
console.log(ite.next());
// done: false(遍历是否完成) value: "a"
迭代器每次使用next()方法之后都会返回一个IteratorResult的对象:
【JavaScript及ES6|ES6---(2)迭代器和生成器】{done: false, value: ‘a’}
- done表示迭代是否完成
- value表示当前值,迭代完成之后为undefined
调用生成器函数会产生一个生成器对象,生成器对象实现iteratorable接口,因此具有next方法。
yield关键字可以让生成器停止和开始执行,生成器函数在遇到yield之前正常执行,遇到yield之后,执行停止,函数作用域保留,函数在生成器对象上调用next()方法恢复执行.需要注意的是yield只能在生成器函数内使用
1、用法
生成器函数与普通函数的区别:
- 在function和函数名之间有一个*
- 只能在函数内部使用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-同步加载 我们来模拟一个场景:
- 页面打开
- 数据加载中,需要时间长,设置settimeout模拟(异步)
- 关闭加载页面
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对象
推荐阅读
- JS中的各种宽高度定义及其应用
- 参保人员因患病来不及到指定的医疗机构就医,能否报销医疗费用()
- MybatisPlus|MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决
- (二)ES6第一节变量(let|(二)ES6第一节变量(let,const)
- 事件代理
- 六步搭建ES6语法环境
- 数组常用方法一
- 【Hadoop踩雷】Mac下安装Hadoop3以及Java版本问题
- 经历了人生,才知道人生的艰难!及精彩!
- 罗塞塔石碑的意义(古埃及文字的起源,圣书体文字是如何被破解的)