生成器的介绍
生成器是ES6中新增的一种函数控制、使用的方案,它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等。平时我们会编写很多的函数,这些函数终止的条件通常是返回值或者发生了异常。
生成器函数也是一个函数,但是和普通的函数有一些区别。
- 首先,生成器函数需要在function的后面加一个符号:*
- 其次,生成器函数可以通过yield关键字来控制函数的执行流程:
- 最后,生成器函数的返回值是一个Generator(生成器):
生成器事实上是一种特殊的迭代器;
生成器函数
第一次执行
function* foo(){
console.log('函数开始执行~');
const value1 = 100
console.log(value1);
yield //第一个next 运行到这里结束
//这里是第二段不执行
console.log('第二段代码');
}
//调用生成器函数时,会给我们返回一个生成器对象 Generator
const generator = foo()
//开始执行第一段代码
console.log('--------------------------');
generator.next()
这样子就算是完整的执行了整个函数
function* foo(){
console.log('函数开始执行~');
const value1 = 100
console.log(value1);
yield //第一个next 运行到这里结束
const value2 = 200
console.log(value2);
yield
const value3 = 300
console.log(value3);
yield
console.log('函数执行结束');
}
//调用生成器函数时,会给我们返回一个生成器对象 Generator
const generator = foo()
console.log('--------------------------');
generator.next()
generator.next()
generator.next()
generator.next()
生成器其实也是一个特殊的迭代器
//当遇到yield时候暂停函数的执行
//当遇到return 直接停止执行
function* foo(){
console.log('函数开始执行');
const value1 = 100
console.log(value1);
yield
const value2 = 200
console.log(value2);
yield
const value3 = 300
console.log(value3);
yield
console.log('函数结束');
return undefined
}
//generator本质上是一个特殊的iterator
const generator = foo()
console.log('返回值:',generator.next() ); //返回值: { value: undefined, done: false }
console.log('返回值:',generator.next() );
console.log('返回值:',generator.next() );
console.log('返回值:',generator.next() ); //{ value: undefined, done: true }
生成器的传参
function* foo(num){
console.log('函数开始执行');
const value1 = 100 * num
console.log('第一段代码:',value1);
const n = yield value1 //value1需要通过yield 接受,这里新的参数n是下面next()传递进来的
const value2 = 200 * n
console.log('第二段代码:',value2);
const count = yield value2
const value3 = count * 0.8
console.log(value3);
yield value3
}
const generator = foo(3)
console.log(generator.next(10)); //这里会打印{ value: 300, done: false } ,并且函数自动执行
generator.next(5)
generator.next(30)
生成器的return 终止方法
function* foo(num){ //第一个得写在这个地方,但是我们很少用
console.log('函数开始执行~');
const value1 = 100 * num //第一段拿到的参数
console.log("第一段代码:",value1)
const n = yield value1 //在这里接受yield的参数,这是第一段的结尾,这样第二段才能用
//generator.return(n)这个位置 return n 提前终止生成器代码
const value2 = 200 * n
console.log("第二段代码:",value2)
// yield value2
const count = yield value2
const value3 = 300 * count
console.log("第三段代码:",value3)
// yield value3
yield
console.log('函数执行结束~');
}
const generator = foo(10)
console.log(generator.next());
//第二段代码的执行
console.log(generator.return(15)); //在第一段与第二段之间提前终止了
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
生成器的throw抛出异常方法
function* foo(){
console.log('代码开始执行');
const value1 = 100
try{
yield value1
}catch(err){
//不能在catch里yield
console.log('捕获到异常情况:',err);
}
console.log('第二段代码继续执行');
const value2 = 200
yield value2
console.log('代码已经结束');
}
const generator = foo()
console.log(generator.next());
console.log(generator.throw('这是异常的信息')); //第二段代码不会执行了
使用生成器来替换迭代器
function* createNamesIterator(arr){
//写法1
// for(const item of arr){
// yield item
// }
//写法2
// let index = 0
// yield arr[index++]
// yield arr[index++]
// yield arr[index++]
//写法3 yield* 加上可迭代对象,yield自动帮我们迭代
yield* arr
}
const names = ["abc","ccc","nba"]
const namesIterator = createNamesIterator(names)
console.log(namesIterator.next());
console.log(namesIterator.next());
console.log(namesIterator.next());
console.log(namesIterator.next());
class的案例
class Classroom{
constructor(address,name,students){
this.address = address
this.name = name
this.students = students
}
entry(newStudent){
this.students.push(newStudent)
}
*[Symbol.iterator]() {
yield* this.students
}
}
const classroom = new Classroom("3懂","1102",["abc","ccc"])
for(const item of classroom){
console.log(item);
}
创建一个函数,这个函数可以迭代一个范围内的数字
function* createRangeIterator(start,end){
// let index = start
// return {
// next(){
// if(index < end){
// return {done:false,value:index++}
// }else{
// return {done:true,value:undefined}
// }
// }
// }
let index = start
while(index < end){
yield index++
}
}
异步函数的处理方案
function requestData(url) {
//异步请求的代码会被放入到executor中
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url)
}, 2000)
})
}
//需求:
//1> url:harry -> harry
//2> url:res + "aaa" -> res:harryaaa
//3> url:res + "bbb" => res:harrybbb
//第一种方案:多次回调
//回调地狱
// requestData("harry").then(res => {
// requestData(res + "aaa").then(res => {
// requestData(res + "bbb").then(res => {
// console.log(res); //6s 以后出来了 harryaaabbb
// //这是回调地狱
// })
// })
// })
//第二种方案:Promise中then的返回值来解决
// requestData("harry").then(res =>{
// return requestData(res+"aaa") //返回一个promise
// }).then(res => {
// return requestData(res + 'bbb')
// }).then(res => {
// console.log(res);
// })
//第三种方案:Promise + generator实现
function* getData(){
//yield requestData("harry") 的返回值是promise
const res1 = yield requestData("harry")
const res2 = yield requestData(res1+"aaa")
const res3 = yield requestData(res2+"bbb")
console.log(res3); ////harryaaabbb
}
const generator = getData()
generator.next().value.then(res => {
generator.next(res).value.then(res => {
generator.next(res).value.then(res => {
generator.next(res)
})
})
})
//这个npm上已经有可以使用的了,包里已经帮我们实现了 叫 co
// function execGenerator(getFn){
// const generator = getFn()
// function exec(res){
// const result = generator.next(res)
// if(result.done) {
// return result.value
// }
// result.value.then(res => {
// exec(res)
// })
// }
// //使用递归
// exec()
// }
// execGenerator(getData)
//3>第三方包 co 自动执行 ,不过这个库已经很久没维护了
// TJ 大神开发的 co 和 n 都是TJ开发的 commander也是他开发的 vue cli内部也是用到了commander
//著名的express框架,koa 也是 TJ开发的 egg是基于koa的
//co大学也不是学计算机的,它是学艺术的
// const co = require('co')
// co(getData)
//4.第四种方案 async/await 在es8中已经有了关键字了
//本质上是由generator演化而来的
async function getData(){
const res1 = await requestData("harry")
const res2 = await requestData(res1+"aaa")
const res3 = await requestData(res2+"bbb")
console.log(res3);
}
getData()