大家好,我是wo不是黄蓉,今年学习目标从源码共读开始,希望能跟着若川大佬学习源码的思路学到更多的东西。
什么是生成器
生成器:是一种函数控制和使用的方案,可以让我们更加灵活的控制函数什么时候继续指向性、暂停执行等
如果已经执行完毕了,后面每次调用next
的返回都是{ value: undefined, done: true }
有什么好处?
可以控制代码的执行,普通函数会将代码都执行一遍
表现形式:
-
函数后面加个*
-
代码执行可以被
yield
控制 -
生成器函数默认在执行时,返回一个生成器对象
- 要想执行函数内部的代码,需要生成器对象,调用他的
Next
操作 - 当遇到
yield
时,会中断执行
- 要想执行函数内部的代码,需要生成器对象,调用他的
举个例子:
function* helloWorldGenerator() {
yield 'hello'
yield 'world'
return 'ending'
}
var hw = helloWorldGenerator()
console.log(hw) //返回一个生成器
console.log(hw.next())
//打印结果
Object [Generator] {}
{ value: 'hello', done: false }
next的时候可以传参
function* helloWorldGenerator() {
console.log('111')
const foo = yield 'hello'
console.log(foo)
const bar = yield 'world'
console.log(bar)
return 'ending'
}
var hw = helloWorldGenerator()
console.log(hw)
console.log(hw.next('i want wo say'))
console.log(hw.next('i want wo say1'))
console.log(hw.next('i want wo say2'))
console.log(hw.next('i want wo say3'))
//打印结果:
Object [Generator] {}
111
{ value: 'hello', done: false }
i want wo say1
{ value: 'world', done: false }
i want wo say2
{ value: 'ending', done: true }
{ value: undefined, done: true }
为什么第一次传参的next
不起作用呢?
因为第一次调用next
执行的代码是第一个yield
之前的代码
参数接收是通过在yield
之前获取的,如果想要第一次的时候传参,需要在调用helloWorldGenerator
里面传参,在函数定义的地方接收参数
function* helloWorldGenerator(name) {
console.log(name)
console.log('111')
const foo = yield 'hello'
console.log(foo)
const bar = yield 'world'
console.log(bar)
return 'ending'
}
var hw = helloWorldGenerator('i want wo say')
console.log(hw)
console.log(hw.next())
//打印结果:
Object [Generator] {}
i want wo say
111
{ value: 'hello', done: false }
生成器函数提前结束
使用throw
或者return
function* getInfos() {
const users = yield getUsers()
console.log('users', users)
throw Error('中断执行')
// return
const menus = yield getMenus()
console.log('menus', menus)
}
const iterator = getInfos()
const result = iterator.next()
result.value
.then((res) => {
if (res.status) {
console.log(res)
}
})
.catch((err) => {
console.log(err)
})
const menus = iterator.next()
menus.value
.then((resMenu) => {
console.log(resMenu)
})
.catch((err) => {
console.log(err)
})
中断执行后,异步代码不会被执行到,在catch
中捕获不到异常
生成器函数解决异步问题,解决回调地狱问题
实现异步:
function getUsers() {
return new Promise((resolve) => {
resolve({
status: true,
list: [
{ name: 'hp', age: 18 },
{ name: 'zhh', age: 18 }
]
})
})
}
function getMenus() {
return new Promise((resolve) => {
resolve({
status: true,
list: [
{ name: '一级菜单-1', pid: 0 },
{ name: '一级菜单-2', pid: 0 }
]
})
})
}
function* getInfos() {
const users = yield getUsers()
console.log('users', users)
// throw Error('中断执行')
// return
const menus = yield getMenus()
console.log('menus', menus)
}
const iterator = getInfos()
const result = iterator.next()
result.value.then((res) => {
if (res.status) {
console.log(res)
}
})
const menus = iterator.next()
menus.value.then((resMenu) => {
console.log(resMenu)
})
打印结果:
现在的代码可以实现按照同步编码的方式实现异步编程,但是每次都需要手动调用Netx
如果上下有依赖关系,类似这样,是不是又有点回调地狱的意思
const iterator = getInfos()
const result = iterator.next()
result.value.then((res) => {
if (res.status) {
console.log(res)
const menus = iterator.next()
menus.value.then((resMenu) => {
console.log(resMenu)
})
}
})
实现co
方法解决手动调用的问题。
function co(fn) {
const iterator = fn()
function handleResult(result) {
//已经迭代完了直接返回,没有迭代完递归调用
if (result.done) return
result.value
.then((res) => {
handleResult(iterator.next(res))
})
.catch((err) => {
console.log(err)
})
}
//默认执行一次
handleResult(iterator.next())
}
co(getInfos)
上面代码使用递归实现依次调用问题
使用生成器实现async/await
使用async/await
async function fetchInfos() {
const res = await getUsers()
console.log(res)
const resMenu = await getMenus()
console.log(resMenu)
}
fetchInfos()
//打印结果:
{
status: true,
list: [ { name: 'hp', age: 18 }, { name: 'zhh', age: 18 } ]
}
{
status: true,
list: [ { name: '一级菜单-1', pid: 0 }, { name: '一级菜单-2', pid: 0 } ]
}
使用generator
实现async/await
function run(gen) {
//把返回值包装成promise
return new Promise((resolve, reject) => {
var g = gen()
function _next(val) {
//错误处理
try {
var res = g.next(val)
} catch(err) {
return reject(err);
}
if(res.done) {
return resolve(res.value);
}
//res.value包装为promise,以兼容yield后面跟基本类型的情况
Promise.resolve(res.value).then(
val => {
_next(val);
},
err => {
//抛出错误
g.throw(err)
});
}
_next();
});
}
关于使用generator实现`async/await`可以[参考](https://juejin.cn/post/6844904096525189128#heading-13)这篇文章,讲的很详细
网友评论