一、Generator的基本概念
1.定义Gernerator函数
Generator是一种函数,这种函数是ES6提出的一种异步编程的解决方案,在它内部,使用yield关键字封装了一个个状态机。这个函数的执行结果,就是一个遍历器对象。
function*next(){yield1;yield2+2;return3;}varfun=next();console.log(fun.next());//{value: 1, done: false}console.log(fun.next());//{value: 4, done: false}console.log(fun.next());//{value: 3, done: true}console.log(fun.next());//{value: undefined, done: true}Generator函数,返回的是一个遍历器对象,这个遍历器对象,拥有next方法,调用这个方法,就能得到yield关键字标识状态的结果。
2.Generator函数特征
- function 关键字和函数之间有一个星号(*),且内部使用yield表达式,定义不同的内部状态
- 调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象
- Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行
3.异步编程
所谓异步,简单说就是一个任务分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。
异步编程方式:
- 回调函数
- 事件监听
- 发布/订阅者
- Promise对象
所谓
回调函数,就是把第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。回调函数的异步方式容易形成多重嵌套,多个异步操作形成了强耦合,只要有一个操作需要修改,它的上层回调函数和下层回调函数,可能都要跟着修改。这种情况就称为”回调函数地狱”(callback hell)。Promise可以解决callback hell问题,Promise对象允许回调函数的嵌套,改成链式调用。
Generator函数之所以是一种完美的异步编程解决方案,本质上是因为它的两个特点:函数体内外的数据交换和完美的错误处理机制。
二、yield 表达式
Generator 函数返回的遍历器对象,只有调用 next 方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。
1.yield 表达式就是暂停标志
yield 表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行
2.使用yield需注意
- yield语句只能用于
function*的作用域,如果function*的内部还定义了其他的普通函数,则函数内部不允许使用yield语句 yield语句如果参与运算,必须用括号括起来
三、next() 方法
1.next 方法参数
表示上一个yield表达式的返回值,所以在第一次使用next方法时,传递参数是无效的。V8 引擎直接忽略第一次使用next方法时的参数,只有从第二次使用next方法开始,参数才是有效的。从语义上讲,第一个next方法用来启动遍历器对象,所以不用带有参数。
2.遍历器对象的 next 方法的运行逻辑
- 遇到
yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。 - 下一次调用
next方法时,再继续往下执行,直到遇到下一个yield表达式。 - 如果没有再遇到新的
yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。 - 如果该函数没有
return语句,则返回的对象的value属性值为undefined。
3.return 方法跟 next 方法的区别
return终结遍历,之后的yield语句都失效;next返回本次yield语句的返回值。return没有参数的时候,返回{ value: undefined, done: true };next没有参数的时候返回本次yield语句的返回值。return有参数的时候,覆盖本次yield语句的返回值,也就是说,返回{ value: 参数, done: true };next有参数的时候,覆盖上次yield语句的返回值,返回值可能跟参数有关(参数参与计算的话),也可能跟参数无关(参数不参与计算)。
4.for…of 循环
可以自动遍历Generator函数时生成的Iterator对象,且此时不再需要调用next方法。一旦next方法的返回对象的done属性为true,for…of循环就会中止,且不包含该返回对象。
5.示例
next()方法
// 定义一个Generator函数function*foo(x){vary=2*(yield(x+1));varz=yield(y/3);return(x+y+z);}vara=foo(5);console.log(a.next());// Object{value:6, done:false} 第一次调用a的next方法时,返回x+1的值6console.log(a.next());// Object{value:NaN, done:false} 第二次运行next方法的时候不带参数,导致y的值等于2 * undefined(即NaN),除以3以后还是NaNconsole.log(a.next());// Object{value:NaN, done:true} 第三次运行Next方法的时候不带参数,所以z等于undefined,返回对象的value属性等于5 + NaN + undefined,即NaN。varb=foo(5);console.log(b.next());// {value:6, done:false } 第一次调用b的next方法时,返回x+1的值6console.log(b.next(12));// {value:8, done:false } 第二次调用next方法,将上一次yield表达式的值设为12,因此y等于24,返回y / 3的值8;console.log(b.next(13));// {value:42, done:true } 第三次调用next方法,将上一次yield表达式的值设为13,因此z等于13,这时x等于5,y等于24,所以return语句的值等于42。Genarator 部署Ajax操作
function*main(){// 通过 Ajax 操作获取数据varresult=yieldrequest("http://some.url");varres=JSON.parse(result);console.log(res.value);}functionrequest(url){makeAjaxCall(url,function(res){it.next(res);})}varit=main();console.log(it.next());Genarator 对任意对象部署 Iterator 接口
function*deployObjectInterface(obj){letkeys=Object.keys(obj);for(leti=0;i<keys.length;i++){letkey=keys[i];yield[key,obj[key]];}}letobj={name:"Cynthia",age:21};for(let[key,value]ofdeployObjectInterface(obj)){console.log(key,value);}// name Cynthia// age 21Genarator 对数组部署 Iterator 接口
function*deployArrayInterface(arr){varnextIndex=0;while(nextIndex<arr.length){yieldarr[nextIndex++];}}vararr=deployArrayInterface(['name','age']);console.log(arr.next());// {value: "name", done: false}console.log(arr.next().value);// nameconsole.log(arr.next().done);// falseconsole.log(arr.next().value);// ageconsole.log(arr.next().done);// trueconsole.log(arr.next().value);// undefinedconsole.log(arr.next().done);// true