Generator函数的概述
Generator 函数是 ES6 提供的一种异步编程解决方案
Generator函数指的是一种新的语法结构,是一个遍历器对象生成器,它内部可以封装多个状态,很适合用于异步操作
Generator函数语法和普通的function函数类似,但是有三个不同点:
(1)function关键字和函数名称之间有一个星号(*)。
(2)函数体内可以使用yield语句。 可暂停函数(惰性求值), yield可暂停,next方法可启动。每次返回的是yield后的表达式结果
(3)函数调用后不会立即执行,返回的是一个遍历器对象
现实业务应用中,Generator的优点是可以等第一条请求发送成功之后,拿到数据在发送第二条请求,缺点是没有脱离回调函数
function* helloWorldGenerator() {
console.log('aaaaaaaaaa')
yield 'hello';
console.log('bbbbbbbbbb')
yield 'world';
console.log('ccccccccccc')
return 'ending';
} let hw = helloWorldGenerator();//调用此函数,并不会立即执行它其中的代码,而是返回一个遍历器对象 也就是指向内部状态的指针对象 // 必须调用遍历器对象的next方法,使得指针移向下一个状态。
// 也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。
// 换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。 // 第一次调用,Generator 函数开始执行,直到遇到第一个yield表达式为止。
// next方法返回一个对象,它的value属性就是当前yield表达式的值hello,done属性的值false,表示遍历还没有结束。
console.log(hw.next()) //{ value: 'hello', done: false }
console.log(hw.next()) //{ value: 'world', done: false }
console.log(hw.next()) //{ value: 'ending', done: true }
console.log(hw.next()) //{ value: undefined, done: true }
console.log(hw.next()) //{ value: undefined, done: true }
创建Generator函数
Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。
不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象
函数内部使用yield语句定义不同的状态,return也可以定义一个状态
每个yield语句定义不同的状态,它也是一个代码执行暂停标识,yield语句不能在普通函数中使用,否则会报错。
// 定义了一个 Generator 函数helloWorldGenerator,它内部有两个yield表达式(hello和world)
// 即该函数有三个状态:hello,world 和 return 语句(结束执行)
function* helloWorldGenerator() {
console.log('aaaaaaaaaa')
yield 'hello';
console.log('bbbbbbbbbb')
yield 'world';
console.log('ccccccccccc')
return 'ending';
} let hw = helloWorldGenerator();//调用此函数,并不会立即执行它其中的代码,而是返回一个遍历器对象 也就是指向内部状态的指针对象 console.log(hw.next()) //{ value: 'hello', done: false },返回一个具有value和done属性的对象
console.log(hw.next()) //{ value: 'world', done: false }
console.log(hw.next()) //{ value: 'ending', done: true } ,有return,返回{value:ending,done:true}
console.log(hw.next()) //{ value: undefined, done: true },没有return,返回{value: undefined, done: true}
console.log(hw.next()) //{ value: undefined, done: true }
yield语句
每一个yield语句定义不同的状态,它也是一个代码执行暂停标识。
yield语句不能在普通函数中使用,否则会报错。
调用Generator函数可以返回一个遍历器对象,要想访问Generator函数中的每一个状态,需要使用遍历器对象调用next()方法
如果yield语句作为其他语句的一部分,那么必须使用小括号包裹,否则会报错
// 定义了一个 Generator 函数helloWorldGenerator,它内部有两个yield表达式(hello和world)
// 即该函数有三个状态:hello,world 和 return 语句(结束执行)
function* helloWorldGenerator() {
//console.log("欢迎来到" + yield "generator");//报错,SyntaxError: Unexpected identifier
console.log("欢迎来到" + (yield "generator"));//正确,打印出:欢迎来到undefined
yield 'hello';
return 'ending';
} let hw = helloWorldGenerator();//调用此函数,并不会立即执行它其中的代码,而是返回一个遍历器对象 也就是指向内部状态的指针对象 console.log(hw.next().value) //generator
console.log(hw.next().value) //hello
console.log(hw.next().value) //ending
next()方法
next()一个主要功能,就是从暂停状态继续下一段代码的执行。
next()还有一个重要的功能,那就是可以接受一个参数,此参数作为上一个yield语句的返回值。
虽然当代码执行到yield语句的时候,能够将其后面的表达式的值作为对象的value属性值,但是默认情况下yield语句是没有返回值的,或者说它的返回值是undefined
function* helloWorldGenerator() {
console.log("欢迎来到" + (yield "generatoraaaaaa"));
console.log("欢迎来到" + (yield "generatorbbbbbb"));
yield 'hello';
return 'ending';
} let hw = helloWorldGenerator(); console.log(hw.next('我是注入进来的aaaa')) //第1个next传值无意义,因为没有上一个yield
console.log(hw.next('我是注入进来的bbbb'))
console.log(hw.next('我是注入进来的cccc'))
console.log(hw.next())
function* yuanku(num) {
let x = 2 * (yield num);
console.log('x='+x);
let y = yield x*3;
console.log('y='+y);
console.log(x,y);
}
var g = yuanku(5);
console.log(g.next());//{value:5,done:false},第1个next传值无意义,因为没有上一个yield
console.log(g.next());//x=NaN {value:NaN,done:false}
console.log(g.next(3));//{value:12,done:false}
console.log(g.next(3));//{value:undefined,done:true}
Generator 函数应用——部署Iterable接口
普通的对象在部署iterable接口之前,是不能使用for of去遍历对象的
let obj = {
userName: 'huang',
age: 32
}
for (let i in obj){ // for in可以遍历普通对象的属性
console.log(i)
} for (let i of obj) { // for of不能遍历普通对象的属性 报错:obj is not iterator
console.log(i)
}
核心思想:可以将任何数据作为Generator函数
的参数,当我们调用Generator函数
返回的Generator类
实例对象就可以认为是参数的遍历器对象,且这个遍历器对象上还有Symbol.iterator
属性方法,方法的返回值指向遍历器对象自己
let obj = {
userName: 'huang',
age: 32
} //相当于给obj这个对象人为的部署了iterator,之后就可以使用for of进行遍历内部的状态
obj[Symbol.iterator] = function* myGenerator () {
yield 1
yield 2
yield 3
} for (let i of obj) {
console.log(i) //遍历了myGenerator函数的状态,默认就是调用next方法,分别是1,2,3
}
function* deployObjectInterface(obj){
let keys = Object.keys(obj);
console.log(keys)
for(let i=0; i<keys.length; i++){
let key = keys[i];
yield [key, obj[key]];
}
}
let obj = {name:"Cynthia", age:21 };
for(let[key, value] of deployObjectInterface(obj)){
console.log(key, value);
}
// name Cynthia
// age 21
Generator 函数应用——部署Ajax操作
模拟一个业务逻辑,首先发送一次请求,成功之后拿到请求回来的数据,再发送第二次请求(这次请求需要用到第一次请求回来的数据)
如果第一条请求没有成功,第二次请求不会触发
function getNews(url) {
$.get(url, function (data) {
console.log(data)
let url = 'http://sss:3000?id='+ data.id
SX.next(url) //第二次请求
})
} function* sendXml() {
yield getNews('http://sss:3000')
yield getNews(url)
}
let SX = sendXml()
SX.next() // 第一次请求