JavaScript及ES6|ES6---(2)迭代器和生成器

一、迭代器 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的对象:
【JavaScript及ES6|ES6---(2)迭代器和生成器】{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());

执行结果:
JavaScript及ES6|ES6---(2)迭代器和生成器
文章图片

第一次调用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对象

    推荐阅读