异步三部曲之Generator

基本写法

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函数的写法就是在关键字functionfunctionName之间加一个 * ,至于空格并未有严格要求,所以gen1gen2gen3都是可行的。

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
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容