Javascript(笔记37) - ES6特性 - 生成器
生成器
生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同;
生成器其实就是一个特殊的函数;
异步编程,纯回调函数,如: node fs ajax mogodb
// 声明一个生成器函数
function* gen() { // 有个 * 很不一样;
console.log('hello generator');
}
let iterator = gen();
console.log(iterator); // gen {<suspended>}
iterator.next(); // hello generator
生成的时候不一样,执行的时候也不一样,生成器函数有个 next 方法,执行这个 next 才会有结果;
另外,在生成器函数,还可以出现 yield 语句;
这个 yield 语句,可以理解为函数代码的分隔符,把函数的语句分割成几块;
function* gen() {
console.log('111');
yield '第一段分隔符';
console.log('222');
yield '第二段分隔符';
console.log('333');
yield '第三段分隔符';
console.log('444');
}
let iterator = gen();
iterator.next(); // 111
iterator.next(); // 222
iterator.next(); // 333
iterator.next(); // 444
这块代码被 yield 分隔符,分割成4段;每执行一次 next 方法,就执行一段语句直到第4次调用 next 方法,把执行完全部语句;
有 next 方法,就可以 for ... of 遍历,试一下:我们先把每一段执行的代码注释掉,看遍历结果:
function* gen() {
yield '第一段分隔符';
yield '第二段分隔符';
yield '第三段分隔符';
}
let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
我们知道 next 方法返回的是一个对象,里面包括 value 和 done ;
也就是说:
第一次调用 next 返回第一个 yield 的值和 done ;这个值目前是一个字符串;
第二次调用 next 返回第二个 yield 的值和 done;
以此类推,直到结束;
生成器参数传递
function* gen(arg){
console.log(arg);
let one = yield '111';
console.log(one);
let two = yield '222';
console.log(two);
let three = yield '333';
console.log(three);
}
let iterator = gen("AAA");
console.log(iterator.next()); // AAA 第一次,返回了实参 AAA;
console.log(iterator.next("BBB")); // BBB 第二次,返回了 one
console.log(iterator.next("CCC")); // CCC 第三次,返回了 two
console.log(iterator.next("DDD")); // DDD 第四次,返回了 three
有了这个特性支持,异步编程参数传递就可以实现了;
第一次调用 next(); 返回的是第一个 yield 以上的代码,即 实参传入的 “AAA”
生成器函数实例一
异步编程,如文件操作,网络操作(ajax,request),数据库操作
需求:1s 后控制台输出 111, 2s 后输出 222, 3s 后输出333;
定时器也是异步的;
setTimeout(()=>{
console.log(111); // 111
setTimeout(()=>{
console.log(222); // 222
setTimeout(()=>{
console.log(333); // 333
},3000);
},2000);
},1000);
这样也能实现需求,但是后面要跟新的异步的话,代码的缩进会不断推进,阅读和调试会很不方便;
这种现象叫:回调地狱;
用生成器函数来实现需求:
function one(){
setTimeout(()=>{
console.log(111); // 111
iterator.next();
},1000);
}
function two(){
setTimeout(()=>{
console.log(222); // 222
iterator.next();
},2000);
}
function three(){
setTimeout(()=>{
console.log(333); // 333
iterator.next();
},3000);
}
function* gen(){
yield one();
yield two();
yield three();
}
let iterator = gen();
iterator.next();
通过初始的 iterator.next() 调用,执行 yield one() ; 再 next 以此类推,逐渐生成结果;
逻辑清晰完整,解决了回调地狱的问题;
生成器函数实例二
模拟获取: 用户数据,订单数据,商品数据
注意获取是有顺序的:即先有用户数据,才能有订单数据,才有商品数据;
function getUsers() {
setTimeout(() => {
let data = "用户数据";
// 调用 next ,并将数据传入
iterator.next(data);
}, 1000);
}
function getOrders() {
setTimeout(() => {
let data = "订单数据";
iterator.next(data);
}, 1000);
}
function getGoods() {
setTimeout(() => {
let data = "商品数据";
iterator.next(data);
}, 1000);
}
function* gen() {
let users = yield getUsers();
console.log(users);
let orders = yield getOrders();
console.log(orders);
let goods = yield getGoods();
console.log(goods);
}
let iterator = gen();
iterator.next();
通过next 传参,最后集中输出数据;跟同步代码很像,实际是异步的,同时也解决了回调地狱问题;