迭代器是ES6提供的一种接口,为不用的数据结构提供统一的访问机制。
用法:
const items = ['a', 'b', 'c'];
let ite = items[Symbol.iterator]();
console.log(ite.next()); // done: false(遍历是否完成) value: "a"
迭代器每次使用next()方法之后都会返回一个IteratorResult的对象:
{done: false, value: ‘a’}
Generator 函数 可以通过yield将函数挂起,为了改变执行流提供了可能,同时为了做异步编程提供了方案。
调用生成器函数会产生一个生成器对象,生成器对象实现iteratorable接口,因此具有next方法。
yield关键字可以让生成器停止和开始执行,生成器函数在遇到yield之前正常执行,遇到yield之后,执行停止,函数作用域保留,函数在生成器对象上调用next()方法恢复执行.需要注意的是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,执行完毕。
我们介绍一个生成器的使用场景:为不具备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}`);
}
我们知道在实际开发环境中,当请求失败的时候可能会一直请求,造成回调地狱。我们可以借助生成器函数避免回调地狱,每次请求结束之后先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)
}
})
}
我们来模拟一个场景:
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对象