基本写法
function *gen1() {
yield 1
}
console.log(gen1()) // gen1 {<suspended>}
// 等价于
function* gen2() {
yield 1
}
console.log(gen2()) // gen2 {<suspended>}
// 等价于
function * gen3() {
yield 1
}
console.log(gen3()) // gen3 {<suspended>}
const genObj = {
*gen(){
yield 1
}
}
console.log(genObj.gen()) // gen {<suspended>}
// 等价于
const genObj1 = {
gen: function *() {
yield 1
}
}
console.log(genObj1.gen()) // gen {<suspended>}
Generator函数的写法就是在关键字function和functionName之间加一个 * ,至于空格并未有严格要求,所以gen1、gen2、gen3都是可行的。
Generator执行方式
console.log('Hi')
function *gen() {
console.log('Hello Generator')
let a = yield 2;
console.log(a)
return a + 1;
yield 3
console.log('Bay Generator')
}
const g = gen()
console.log('Bay')
// Hi
// Bay
console.log(g.next())
// Hello Generator
// {value: 2, done: false}
console.log(g.next())
// undefined
// {value: NaN, done: true}
console.log(g.next())
// {value: undefined, done: true}
console.log(g.next())
// {value: undefined, done: true}
暴躁老哥:那yield是个啥玩意?
yield是一个标记,用来告诉Generator函数到这停一下,并且yield默认是不返回值的。
上面的例子可以看到Generator函数在执行之后只会返回一个指针对象,内部的代码并不会执行,当调用next时才开始执行,next执行后会把指针指向下一步,执行两个指针中间的部分。同时return也代表一个指针位置,并且还是具有停止效果。
next返回{ value: any, done: Bollean },done用来告诉我们遍历是否完成。value代表当前值。
暴躁老哥:能不能说点我听得懂的?
Generator函数就像公交车一样,执行之后得到路线,next方法告诉它:你该去下一站了,你不告诉它就不动,哎~就是玩。done就是表示有没有到终点,value表示到了哪一站。
Generator.prototype.next 方法
next 方法就是指针移到下一个yield,同时next可以传一个参数,用来指定上一个yield的返回值。
function *gen() {
let a = yield 2;
console.log(a)
return a + 1;
}
const g = gen()
console.log(g.next())
// {value: 2, done: false}
console.log(g.next(3))
// 3
// {value: 4, done: true}
第一次调用next传入参数是无效的,因为它代表开始,前面并没有yield。上面例子可以看到,第一次调用next开始执行,遇到yield就停止了,同时把值传了出来,第二次调用时传入了3,所以a就变成了3,最后返回的就是4
Generator.prototype.throw 方法
throw方法的作用就是让Generator函数在这一步抛出一个错误,可在内部捕获,也可在外部捕获。
function *gen() {
let a = 0
try{
yield a = 1
} catch(err) {
console.log('内部捕获', err)
}
console.log(a)
}
const g1 = gen()
console.log(g1.next())
// {value: 1, done: false}
console.log(g1.throw('来个红色'))
// 内部捕获 来个红色
// 1
// {value: undefined, done: true}
try{
console.log(g1.throw('再来个红色'))
} catch(err) {
console.log('外部捕获', err)
}
// 外部捕获 再来个红色
throw方法会在上一个指针处抛出一个错误,当内部没有进行捕获时,就会被外部捕获,如果外部也没有进行捕获,那么程序就会出错
Generator.prototype.return 方法
return方法会使Generator函数停止运行,并返回传入的值
function *gen() {
console.log('go');
yield 1
console.log('第一站')
yield 2
console.log('第二站')
yield 3
console.log('第三站')
}
const g = gen();
g.next() // {value: 1, done: false}
// go
g.next() // {value: 2, done: false}
// 第一站
g.next() // {value: 3, done: false}
// 第二站
g.next() // {value: undefined, done: true}
// 第三站
const g1 = gen();
g1.next() // {value: 1, done: false}
// go
g1.return('提前结束') // {value: 提前结束, done: true}
g1.next() // {value: undefined, done: true}
g1.next() // {value: undefined, done: true}
自动执行
function *gen() {
yield 1
yield 2
yield 3
return 4
}
const g = gen();
let next = g.next()
console.log(next.value)
while(!next.done){
next = g.next();
console.log(next.value)
}
// 1
// 2
// 3
// 4
const g1 = gen()
console.log([...g1])
// [1, 2, 3]
for(let v of gen()){
console.log(v)
}
// 1
// 2
// 3
当都是同步操作时可直接遍历指针对象,但是此操作会忽略 return。调用 next 方法可获取到 return 值。
yield* 语法
function *gen1() {
yield 'a'
yield 'b'
return 'ab'
}
function *gen() {
yield 1
yield 2
yield* gen1()
yield 3
return 4
}
const g = gen();
let next = g.next()
console.log(next.value)
while(!next.done){
next = g.next();
console.log(next.value)
}
// 1
// 2
// a
// b
// 3
// 4
yield*语法可以把别的Generator函数进行执行,yield会返回一个指针对象
简易异步自动执行
配合Promise实现处理异步并自动执行的一个简单案例
function thunk(fn, ...args) {
const ctx = this;
return function (callback) {
try {
const res = fn.apply(ctx, args)
return res
} catch (err) {
callback(err);
}
}
}
function run(gen, ...arg) {
const g = gen(...arg);
function next(err, data) {
err && (console.error(err))
let result = g.next(data);
if (result.done) return;
const { value, done } = result;
if(value.then) {
value.then(res => {
next(null, res)
})
}else if(typeof value === 'function') {
Promise.resolve(value(conso.error)).then(res => {
next(null, res)
})
}else {
next(value)
}
}
next();
}
function test() {
return new Promise((resolve, reject) => {
resolve(1)
}).then(res => {
console.log(1)
return res
})
}
function *testSaga(time) {
let resoult = {}
yield new Promise(function(resolve, reject) {
setTimeout(() => {
resolve({
saga: 2,
list: ['hello']
})
}, time)
}).then(res => {
resoult = res
})
console.log('resoult', resoult)
const b = yield thunk(test);
console.log('b', b)
}
run(testSaga, 1000)
// resoult {saga: 2, list: Array(1)}
// 1
// b 1