开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情
前言
在看这篇文章之前,大家需明白什么是迭代器,再学习本篇文章。可点击此链接学习:juejin.cn/post/720593…
一. 什么是生成器?
生成器是ES6中新增的一种函数控制使用方案,可以灵活控制函数执行的行为,其也是一个函数,定义规则为function* XXX(),通过yield关键字来控制函数的执行流程,返回值是一个Generator(生成器)。
举例:
function* foo() {
console.log("函数开始执行~")
const value1 = 100
console.log("第一段代码:", value1)
yield //执行到该位置即暂停
const value2 = 200
console.log("第二段代码:", value2)
const value3 = 300
console.log("第三段代码:", value3)
console.log("函数执行结束~")
}
// 调用生成器函数时, 会给我们返回一个生成器对象
const generator = foo()
// 开始执行第一段代码
generator.next()
大家可以在不同的代码位置加入yield 进行测试,如果不写入yield该结果会以默认顺序执行。
1. 生成器传递参数
上方既然可以暂停来分段执行,那么函数是可以传递参数的,我们可以给每个分段来传递参数。
举例:
function* foo(num) {
console.log("函数开始执行~")
const value1 = 100 * num
console.log("第一段代码:", value1)
const n = yield value1
const value2 = 200 * n
console.log("第二段代码:", value2)
const count = yield value2
const value3 = 300 * count
console.log("第三段代码:", value3)
yield value3
console.log("函数执行结束~")
return "123"
}
// 生成器上的next方法可以传递参数
const generator = foo(5)
console.log(generator.next())
// 第二段代码, 第二次调用next的时候执行的
console.log(generator.next(1))
console.log(generator.next(2))
2. 生成器return 终止 和 throw抛出异常
生成器自带return方法调用即可终止迭代。
举例:
function* foo(num) {
console.log("函数开始执行~")
const value1 = 100 * num
console.log("第一段代码:", value1)
const n = yield value1
const value2 = 200 * n
console.log("第二段代码:", value2)
const count = yield value2
const value3 = 300 * count
console.log("第三段代码:", value3)
yield value3
console.log("函数执行结束~")
return "123"
}
const generator = foo(10)
console.log(generator.next())
// 第二段代码的执行, 使用了return
// 那么就意味着相当于在第一段代码的后面加上return, 就会提前终端生成器函数代码继续执行
console.log(generator.return(15))
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
当然也可以给生成器函数内部抛出异常: 抛出异常后我们可以在生成器函数中捕获异常; 但是在catch语句中不能继续yield新的值了,但是可以在catch语句外使用yield继续中断函数的执行
举例:
function* foo() {
console.log("代码开始执行~")
const value1 = 100
try {
yield value1
} catch (error) {
console.log("捕获到异常情况:", error)
yield "abc"
}
console.log("第二段代码继续执行")
const value2 = 200
yield value2
console.log("代码执行结束~")
}
const generator = foo()
const result = generator.next()
generator.throw("error message")
二.生成器代替迭代器
我们知道生成器是一种特殊的迭代器,特殊情况下我们可以用生成器代替迭代器使用。
yield*是一种yield的语法糖,只不过会依次迭代这个可迭代对象,每次迭代其中的一个值
举例:
// 生成器来替代迭代器
function* createArrayIterator(arr) {
// 3.第三种写法 yield*
// yield* arr
// 2.第二种写法
// for (const item of arr) {
// yield item
// }
// 1.第一种写法
yield "abc" // { done: false, value: "abc" }
yield "cba" // { done: false, value: "cba" }
yield "nba" // { done: false, value: "nba" }
}
const names = ["abc", "cba", "nba"]
const namesIterator = createArrayIterator(names)
console.log(namesIterator.next())
console.log(namesIterator.next())
console.log(namesIterator.next())
console.log(namesIterator.next()) //{ value: undefined, done: true }
//函数生成器写法
function* createRangeIterator(start, end) {
let index = start
while (index < end) {
yield index++
}
//第一种写法
// let index = start
// return {
// next: function() {
// if (index < end) {
// return { done: false, value: index++ }
// } else {
// return { done: true, value: undefined }
// }
// }
// }
}
const rangeIterator = createRangeIterator(10, 20)
console.log(rangeIterator.next())
console.log(rangeIterator.next())
//类生成器写法
class Classroom {
constructor(address, name, students) {
this.address = address
this.name = name
this.students = students
}
entry(newStudent) {
this.students.push(newStudent)
}
foo = () => {
console.log("foo function")
}
// [Symbol.iterator] = function*() {
// yield* this.students
// }
*[Symbol.iterator]() {
yield* this.students
}
}
const classroom = new Classroom("3幢", "1102", ["abc", "cba"])
for (const item of classroom) {
console.log(item)
}
三. 生成器处理异步代码
假设模拟网络请求:
// request.js
function requestData(url) {
// 异步请求的代码会被放入到executor中
return new Promise((resolve, reject) => {
// 模拟网络请求
setTimeout(() => {
// 拿到请求的结果
resolve(url)
}, 2000);
})
}
- 我们可以用promise+generator实现:
function* getData() {
const res1 = yield requestData("hh")
const res2 = yield requestData(res1 + "aaa")
const res3 = yield requestData(res2 + "bbb")
const res4 = yield requestData(res3 + "ccc")
console.log(res4)
}
手动执行生成器实现:
const generator = getData()
generator.next().value.then(res => {
generator.next(res).value.then(res => {
generator.next(res).value.then(res => {
generator.next(res)
})
})
})
自己可以封装自动执行函数:
function execGenerator(genFn) {
const generator = genFn()
function exec(res) {
const result = generator.next(res)
if (result.done) {
return result.value
}
result.value.then(res => {
exec(res)
})
}
exec()
}
// execGenerator(getData)
// execGenerator(getDepartment)
总结
当然我们处理异步代码显然async ,await更好的方式,更简洁,更快速。