调查:
阮一峰先生es6入门里面直接写 Generator 函数 ,一般人直接就懵了,这是什么?
我查询了廖雪峰,阮一峰还有开课吧的相关资料以后,终于得出了一个比较靠谱的结论:
构造器函数是一种,可以执行到一部分就停止的函数,每次调用,执行其中的一部分。由
yield关键字控制每次调用的部分。
揭秘:
它能够实现一个什么目的呢?就是多个函数配合协同作战,第一个函数,执行到一半,交给第二个函数,第二个执行完了,再转交给第一个函数继续运行。用另一种方式实现了队列概念。因为一般来说,单线程编程,函数从上而下执行,是不能到一半中断的,两个函数配合就没法完成了,但是有了构造器函数,一种行为变成了可能。
(但是你要注意的是,这个配合,仍然属于单线程配合,如果想要实现多线程,可以使用worker。单线程协作,是一种折中的解决方案。)
先看简单例子: 构造器函数其实非常简单,再function后面加一个*就行了:
function * fun(){
yield 1+2 //第一次执行返回
yield 2+3 //第二次执行返回
}
let g=fun()
console.log(g.next()); //{value: 3, done: false}
console.log(g.next()); //{value: 5, done: false}
console.log(g.next()); //{value: undefined, done: true}
由这个例子可以看出来,构造器函数每次都返回一个对象,value是执行语句后得到的值,done是执行完毕没有。
传参:
但是我们最经常遇到的情况应该是下面这种:
需要把第一部分执行完,得到的结果传入后面的函数,卧槽。那特么就十分的尴尬了。
请看这个函数,尤其注意a,在函数的第二句中,a并不代表上面的let a,而是代表传进来的
function * fun(){
let a=yield 1+2 //第一次执行返回
let b=yield 1+3+a //第二次执行返回
}
let g=fun();
let g1=g.next();
let g2=g.next(g1.value);
console.log(g1); //{value: 3, done: false}
console.log(g2); //{value: 7, done: false}
延申1:多个函数协程
我必须说,真是伟大的构造器函数,这一步充满了想象力。
你必须从这个例子中才能感受到两个函数配合的牛逼之处。
通过执行到一部分内容 突然用yield截断后返回一个内容,然后再通过判断返回的内容
决定用哪个函数继续运行。
let houyi=function*(){
console.log("开始准备!我是houyi在执行");
yield "x"
console.log("进入第四步!我是houyi在执行");
yield "s"
}
let xiahou=function*(){
console.log("进入第二步,我是xiahou在执行");
yield "h"
console.log("进入第三步!我是xiahou在执行");
yield "z"
}
let h=houyi();
let x=xiahou();
//流程控制
function run(gen){
var v = gen.next();
switch(v.value){
// 发现是x运行x函数
case "x":
run(x);
break;
// 发现是h继续运行x函数
case "h":
run(x);
break;
// 发现是z继续运行x函数
case "z":
run(h);
break;
}
}
//开始执行
run(h);
延申2:解决多个异步队列
// 声明3个异步函数
let fun1=()=>{
setTimeout(()=>{
console.log("第一步");
},1000)
}
let fun2=()=>{
setTimeout(()=>{
console.log("第二步");
},800)
}
let fun3=()=>{
setTimeout(()=>{
console.log("第三步");
},600)
}
fun1();
fun2();
fun3();
用构造器函数解决多个异步函数,
理论上异步队列应该用promise方式去处理,构造器函数不是干这个用的,
但是复杂的情况谁也不好说,这方方式也应该熟悉。
// 声明3个异步函数
let fun1=(sucess)=>{
setTimeout(()=>{
console.log("第一步");
console.log(sucess);
sucess();
},1000)
}
let fun2=(sucess)=>{
setTimeout(()=>{
console.log("第二步");
sucess();
},800)
}
let fun3=(sucess)=>{
setTimeout(()=>{
console.log("第三步");
sucess();
},600)
}
// 创建顺序构造器函数
// 这里主要是利用了构造器每次只能执行一部分的原理
let shunxu=function*(){
yield fun1;
yield fun2;
yield fun3;
}
// 执行函数 把构造器传进来通过next运行即可
let run=(fn)=>{
const go = fn();
function yunxing(){
const gofun = go.next();
if(gofun.done) return; //.next返回done的值为ture时说明已经结束 此时跳出函数
// 这里容易让人费解
// 其实gofun表示的next()对象 它的value是当前运行
// 的那个函数,此时再把yunxing函数传进入执行,其实跟回调也没什么区别了
gofun.value(yunxing)
}
yunxing();
}
run(shunxu);