Generator函数
- Generator函数是ES6提供的一种异步编程解决方案。
- Generator函数是一个状态机,封装了多个内部状态。
- 执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态。
1.基本语法
2.next的参数
3.Generator的实际应用
function *gen(){ // Generator的写法
console.log("111")
yield //产出
console.log("222")
yield
console.log("333")
}
gen()
使用*号即可生成Generator函数,但是上面的代码执行结果为空,控制台中什么也不会打印。因为Generator是不会主动执行的,它需要驱动,相当于给车加油,.next就是汽油。
function *gen(){
console.log("111")
yield //产出
console.log("222")
yield
console.log("333")
}
let g = gen()
g.next()
这样就可以完美执行了吗,答案是不会
打印了111之后就不会继续执行了,因为碰到了yield,它相当于中断器,车开到这直接没油了,所以需要再次加油
function *gen(){
console.log("111")
yield //产出
console.log("222")
yield
console.log("333")
}
let g = gen()
g.next()
g.next()
执行结果
又碰到yield又没油了,再来一次
function *gen(){
console.log("111")
yield //产出
console.log("222")
yield
console.log("333")
}
let g = gen()
g.next()
g.next()
g.next()
当三次调用.next函数才全部打印,继续调用next是不会继续打印的。
注意注释,我给yield标的一个产出,说明它不仅能中断,还能返回东西
function *gen(){
console.log("111")
yield "aaa"//产出
console.log("222")
yield "bbb"
console.log("333")
}
let g = gen()
let res1 = g.next()
console.log(res1)
let res2 = g.next()
console.log(res2)
let res3 = g.next()
console.log(res3)
这里可以发现,yield返回的结果像什么,Iterator迭代器,所以它一定是可以使用for of的。
function* gen() {
console.log("111")
yield "aaa"//产出
console.log("222")
yield "bbb"
console.log("333")
}
let g = gen()
for (let i of g) {
console.log(i)
}
let res1 = g.next()
console.log(res1)
let res2 = g.next()
console.log(res2)
let res3 = g.next()
console.log(res3)
这里因为for of执行时调用了.next,所以后面在执行.next时,执行结果就为空了。
二、next的参数
function* gen() {
let res1 = yield "aaa"
console.log("gen函数内部",res1)
let res2 = yield "bbb"
console.log("gen函数内部",res2)
}
let g = gen()
let res1 = g.next("next传入111")
console.log(res1)
let res2 = g.next("next传入222")
console.log(res2)
let res3 = g.next("next传入333")
console.log(res3)
这里打印的结果让人摸不着头脑,慢慢看打印顺序,因为执行到第一个yield函数中断了,所以相当于把gen函数内部的“aaa”返回给外部的res1时,就中断了,给函数内部的res1赋值并没有完成,所以gen函数内部的res1需要第二个next参数传值才能有效赋值。所以第一个next的传参无意义。
三、Generator的实际应用
function ajax(url){
return new Promise((resolve,reject) => {
let xhr = new XMLHttpRequest()
xhr.open("get",url,true)
xhr.send()
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status<300){
resolve(JSON.parse(xhr.responseText))
}else{
reject(xhr.responseText)
}
}
}
})
}
function* gen(){
let res1 = yield ajax("01.json")
console.log("第一个请求的结果",res1)
let res2 = yield ajax("02.json",res1)
console.log("第二个请求的结果",res2)
}
let g = gen()
g.next().value.then(data => {
console.log(data,"这是ajax请求回来的数据")
g.next(data).value.then(data2 => {
console.log(data2,"这是ajax通过第一次请求回来的数据请求的第二次数据")
g.next(data2)
})
})
上面的代码简单封装了一个ajax,和使用了json-server来模拟数据请求。模拟实现了在第一次请求后,通过第一次请求的数据发起下一次请求。
但是在调用next时,要一直循环嵌套,所以不可避免的又出现了回调地狱。这个问题由后面的 async和await解决,其实本质是一样的,async相当于yield,await相当于这了next,现在需要调用next进行手动驱动,而async,await相当于自动驱动,无非是手动挡变自动挡,但是原理是一样的。
function ajax(url){
return new Promise((resolve,reject) => {
let xhr = new XMLHttpRequest()
xhr.open("get",url,true)
xhr.send()
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status<300){
resolve(JSON.parse(xhr.responseText))
}else{
reject(xhr.responseText)
}
}
}
})
}
function* gen(){
let res1 = yield ajax("01.json")
console.log("第一个请求的结果",res1)
let res2 = yield ajax("02.json",res1)
console.log("第二个请求的结果",res2)
}
//手动版本
// let g = gen()
// g.next().value.then(data => {
// console.log(data,"这是ajax请求回来的数据")
// g.next(data).value.then(data2 => {
// console.log(data2,"这是ajax通过第一次请求回来的数据请求的第二次数据")
// g.next(data2)
// })
// })
//自动版本
function AutoRun(gen){
let g = gen();
function next(data){
let res = g.next(data);
if(res.done) return
res.value.then(function (data) {
next(data);
})
}
next()
}
AutoRun(gen)
在await出现之前的自动挡,可以说是相当好开了!!!后面的async和await其实就是对generator的封装而已,就是语法糖。