es6——Generator

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同

特征:

  • function 命令与函数名之间有一个星号
  • 函数体内部使用 yield 语句定义不同的内部状态
  function * f () {
    yield 'hello'
    yield 'world'
    return '!'
  }

  let fg = f()

  // 调用遍历器对象的 next 方法,使得指针移向下一个状态,直到遇到下一条 yield 语句(或 return 语句)为止
  // done 为 true 时遍历结束
  console.log(fg.next())// {value: "hello", done: false}
  console.log(fg.next())// {value: "world", done: false}
  console.log(fg.next())// {value: "!", done: true}
  console.log(fg.next())// {value: undefined, done: true}
yield 表达式

Generator 函数内部提供了一种可以暂停执行的函数,yield 语句就是暂停标志
遍历器对象的 next 方法的运行逻辑如下:

  • 遇到 yield 语句就是暂停执行后面的操作,并将紧跟在 yield 后的表达式的值作为返回的对象的 value 值
  • 下次调用 next 方法继续向下执行后面的 yield 语句
  • 直到 return 为止,将 return 的值赋值给 value,若无 return 后面的值 value 都为 undefined,此时 done 值为 true,for of 遍历停止

注意:yield 表达式只能用在 Generator 函数里面,用在其他地方都会报错

  // yield 表达式如果用在另一个表达式之中,必须放在圆括号里
  function * f () {
    console.log('hello' + yield)// error
    console.log('hello' + yield 123)// error

    console.log('hello' + (yield))// ok
    console.log('hello' + (yield 123))// ok
  }
  // yield 表达式用作函数参数或放在赋值表达式右边,可以不加括号
  function * f () {
    foo(yield 'a', yield 'b')// ok
    let input = yield// ok
  }

Generator 内部调用 Generator:

注意:任何数据只要具有 Iterator 接口,就可以被 yield* 遍历

  function * f1 () {
    yield 'c'
    yield 'd'
  }

  function * f2 () {
    yield 'a'
    yield 'b'
    // yield * ['c', 'd'] 会达到一样的效果
    yield * f1()
    yield 'e'
  }

  for (let i of f2()) {
    console.log(i)// a  b  c  d  e
  }
next 传参
function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}
 
var b = foo(5);
b.next()   // { value:6, done:false }
b.next(12) // { value:8, done:false } 
b.next(13) // { value:42, done:true }

重点:

next方法的参数表示上一个yield表达式的返回值!!不是函数中参数(如本例的x)的值!不可混淆!!!

代码第一次调用b的next方法时,x=5,返回x+1的值6
第二次调用next方法,将上一次yield表达式的值设为12,即 yield(x+1)==> 12,因此y等于24,z=y / 3为8
第三次调用next方法,将上一次yield表达式的值设为13,即 yield(y/3) ==> 13,因此z等于13,这时x等于5,y等于24,所以return语句的值等于42

调用 Generator 函数返回一个遍历器对象
  function * f () {
    yield 'hello'
    yield 'world'
    yield '!'
  }

  let fg = f()

  for (let i of fg) {
    console.log(i)// hello world !
  }

  // 任何一个对象的 Symbol.iterator 方法等于该对象的遍历器对象生成函数,调用该函数会返回该对象的一个遍历器对象
  // 由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的 Symbol.iterator 属性,从而使得该对象具有 Iterator 接口
  let fg2 = {}
  
  fg2[Symbol.iterator] = function * () {
    yield 'hello'
    yield 'world'
    yield '!'
  }

  console.log([...fg2])// (3) ["hello", "world", "!"]
实例
  • generator 异步请求
  // 嵌套 axios
  this.$http.get('/data1', data1, (res, err) => {
    if (err) {
      return handle(err)
    }
    this.$http.get('/data2', data2, (res, err) => {
      if (err) {
        return handle(err)
      }
      return success(res)
    })
  })
  // generator
  try {
    let r1 = yield this.$http.post('/data1', data1)
    let r2 = yield this.$http.post('/data2', data2)
    success(r2)
  } catch (e) {
    handle(e)
  }
  • 异步读取文件
// Thunk 方法
const fs = require('fs')

// Thunk 函数 :用于 Generator 函数的自动流程管理
// 推荐使用 thunkify 模块 ,地址 https://github.com/tj/node-thunkify
const Thunk = function (fn) {
  return function () {
    let args = Array.prototype.slice.call(arguments)
    return function (callback) {
      args.push(callback)
      return fn.apply(this, args)
    }
  }
}

let readFile = Thunk(fs.readFile)

// 基于 Thunk 函数的 Generator 执行器
// 推荐使用 co 模块 ,地址 https://github.com/tj/node-thunkify
function run (fn) {
  let gen = fn()

  function next (err, data) {
    let result = gen.next(data)
    if (result.done) return
    result.value(next)
  }

  next()
}

// Generator 执行异步文件读取
function * g () {
  try {
    let data1 = yield readFile('./data1.json')

    let data2 = yield readFile('./data2.json')

    let data3 = yield readFile('./data3.json')

    console.log(data1.toString(), data2.toString(), data3.toString())
  } catch (e) {
    console.log(e)
  }
}

// 执行 Generator 函数
// 注意:使用执行器或 co 的前提是 yield 后面只能是 Thunk 函数或 Promise 对象
run(g)
// Promise 方法
const fs = require('fs')

// Promise 函数 :用于 Generator 函数的自动流程管理
// 推荐使用 thunkify 模块 ,地址 https://github.com/tj/node-thunkify
function promise (fn) {
  return new Promise(((resolve, reject) => {
    fs.readFile(fn, (err, data) => {
      if (err) return reject(err)
      resolve(data)
    })
  }))
}

const readFile = promise

// 基于 Thunk 函数的 Generator 执行器
// 推荐使用 co 模块 ,地址 https://github.com/tj/node-thunkify
function run (fn) {
  let gen = fn()

  function next (data) {
    let result = gen.next(data)
    if (result.done) return result.value
    result.value.then((data) => {
      next(data)
    })
  }

  next()
}

// Generator 执行异步文件读取
function * g () {
  try {
    let data1 = yield readFile('./data1.json')

    let data2 = yield readFile('./data2.json')

    let data3 = yield readFile('./data3.json')

    console.log(data1.toString(), data2.toString(), data3.toString())
  } catch (e) {
    console.log(e)
  }
}

// 执行 Generator 函数
// 注意:使用执行器或 co 的前提是 yield 后面只能是 Thunk 函数或 Promise 对象
run(g)
  • 执行 Generator 并取值
function * fun () {
  yield 1
  yield 2
  yield 3
  yield 4
  yield 5
}

function run (fn, resolve) {
  let gen = fn()

  function next (arr = []) {
    let result = gen.next()
    if (result.done) return resolve(arr)
    arr.push(result.value)
    next(arr)
  }

  next()
}

run(fun, data => {
  console.log(data)// (5) [1, 2, 3, 4, 5]
})
  • 斐波那契数列
  function * f () {
    let [oldVal, newVal] = [0, 1]
    for (; ;) {
      [oldVal, newVal] = [newVal, oldVal + newVal]
      yield newVal
    }
  }

  for (let i of f()) {
    if (i > 1000) break
    console.log(i)
  }
  • for of 遍历对象封装
  function * f1 (obj) {
    let keys = Reflect.ownKeys(obj)
    for (let key of keys) {
      yield [key, obj[key]]
    }
  }

  let obj = { a: '1', b: '2', c: '3' }

  for (let [key, value] of f1(obj)) {
    console.log(key, value)
  }
  
  // 另一种写法:将 Generator 函数添加到对象的 Symbol.iterator 属性上
  function * f1 () {
    let keys = Object.keys(this)

    for (let key of keys) {
      yield [key, this[key]]
    }
  }

  let obj = { a: '1', b: '2', c: '3' }
  obj[Symbol.iterator] = f1

  for (let [key, value] of obj) {
    console.log(key, value)
  }
  • 其他方法调用 Generator 接口
  function * f () {
    yield 1
    yield 2
    yield 3
  }

  // 扩展运算符
  console.log([...f()])// (3) [1, 2, 3]

  // Array.from 方法
  console.log(Array.from(f()))// (3) [1, 2, 3]

  // 解构赋值
  let [x, y, z] = f()
  console.log(x, y, z)// 1  2  3
  • 遍历嵌套数组的所有成员
  function * iterTree (tree) {
    if (Array.isArray(tree)) {
      for (let i = 0; i < tree.length; i++) {
        yield * iterTree(tree[i])
      }
    } else {
      yield tree
    }
  }

  const tree = ['a', ['b', 'c'], ['d', 'e', ['f', 'g']]]

  for (let x of iterTree(tree)) {
    console.log(x)// a  b  c  d  e  f  g
  }
  • 遍历完全二叉树
  // 二叉树构造函数,三个参数分别为左树、当前节点、右树
  function Tree (left, label, right) {
    this.left = left
    this.label = label
    this.right = right
  }

  // 下面是中序遍历函数
  // 由于返回的是一个遍历器,所以要用 generator 函数
  // 函数体内采用递归算法,所以左树和右树要用 yield* 遍历
  function * inorder (t) {
    if (t) {
      yield * inorder(t.left)
      yield t.label
      yield * inorder(t.right)
    }
  }

  // 生成二叉树
  function make (arr) {
    // 判断是否为叶节点
    if (arr.length === 1) return new Tree(null, arr[0], null)
    return new Tree(make(arr[0]), arr[1], make(arr[2]))
  }

  let tree = make([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]])

  // 遍历二叉树
  let result = []
  for (let node of inorder(tree)) {
    result.push(node)
  }

  console.log(result)// (7) ["a", "b", "c", "d", "e", "f", "g"]
  • 状态机
  let clock = function * () {
    while (true) {
      console.log('0')
      yield
      console.log('1')
      yield
    }
  }

  let status=clock()

  status.next()// 0
  status.next()// 1
  status.next()// 0
  status.next()// 1
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值