Generator

Friday, August 23, 2019

Generator 生成器

  1. Genarator 不是函数
  2. 执行var h = generator()后,内部代码不会立即执行,而是处于暂停状态
  3. 执行h.next()后会激活状态,开始执行内部语句,直到遇到yield,会执行yield后的表达式并返回结果,然后又进入暂停状态
  4. 遇到return预示执行结束,返回的done值为true,无法继续执行
  5. 继续执行h.next(),value就会为undefined
  6. 每次执行h.next()都会打破暂停状态,直到遇到下一个yieldreturn
  7. h.next()返回值永远是{value:...,done:....}的形式

Iterator 遍历器

数组,类数组对象,Set,Map 原生具有[Symbol.iterator]属性,可以使用for...of取值

const arr = [1, 2, 3, 4]
const iterator = arr[Symbol.iterator]()

[Symbol.iterator]可以生成一个 Iterator 对象

// next使用
console.log(iterator.next())  // { value: 1, done: false }
...
console.log(iterator.next())  // { value: 4, done: true }
console.log(iterator.next())  // { value: undefined, done: true }
// for...of使用
let i
for (i of iterator) {
    console.log(i) // 1 2 3 4
}

Generator 返回的也是 Iterator 对象

function* generator() {
    yield 100
    yield (function () {
        return 200
    })()
    return 300
}
const g = generator()
console.log(g.next()) { value: 100, done: false }
for( let i of g ) {
    console.log(i)
}

Generator 具体应用

yield 具有传递数据的功能,通过 next,会将yield后表达式的值传递至value

next 向 yield 传递值,则是传到上一个执行完的 yield 语句前的变量中

var a = yield 200
var b = yield 200
g.next()
g.next(300)    //传 到了变量a中

使用 yield*语句可以嵌套 Genarator

function* g1() {
    yield 1
    yield* g2()
    yield 4
}
function* g2() {
    yield 2
    yield 3
} //

1,2,3,4 顺序

Thunk 函数

任何函数,只要有回调函数,就可以写成 Thunk 函数的形式

Thunk 函数:对多参数的函数进行封装,返回一个只接受一个回调函数参数的函数.

var Thunk = function (fn) {
    return function (...args) {
        return function (callback) {
            args.push(callback)
            return fn.apply(this, args)
        }
    }
}
var readFileThunk = Thunk(fs.readfile)
readFileThunk(fileA)(callback)

使用 thunkify 库

const thunk = thunkify(fs.readFile)
const readFileThunk = thunk('data1.json', 'utf-8')
readFileThunk((err, data) => {
    // 获取文件内容
})

Generator 与异步操作

const readFileThunk = thunkify(fs.readFile)
const generator = function* () {
    const r1 = yield readFileThunk('data1.json') //此时value等于Thunk函数,需要一个回调函数
    const r2 = yield readFileThunk('data2.json')
}
const g = generator()
g.next().value((err, data1) => {
    g.next(data1).value((err, data2) => {
        //给r1传值,并且执行next,传入回调函数
        g.next(data2) //给r2传值,执行next
    })
})

自驱动

function run(generator) {
    const g = generator()
    function next(data) {
        var res = g.next(data)
        if (res.done) {
            return res.value
        }
        res.value.then(function (data) {
            //需要yield返回一个promise对象
            next(data)
        })
    }
    next()
}
run(generator)

co 库

const co = require('co')
const readFileThunk = thunkify(fs.readFile)
const gen = function* () {
    const r1 = yield readFileThunk('data1.json')
    console.log(r1.toString())
    const r2 = yield readFileThunk('data2.json')
    console.log(r2.toString())
}
const c = co(gen) //返回一个promise对象

Generator的本质

介绍Generator中,多次提到 暂停 这个词 ———— “暂停”才是Generator的本质 ———— 只有Generator能让一段程序执行到指定的位置先暂停,然后再启动,再暂停,再启动。

而这个 暂停 就很容易让它和异步操作产生联系,因为我们在处理异步操作时,即需要一种“开始读取文件,然后暂停一下,等着文件读取完了,再干嘛干嘛…”这样的需求。因此将Generator和异步操作联系在一起,并且产生一些比较简明的解决方案,这是顺其自然的事儿,大家要想明白这个道理。

不过,JS 还是 JS,单线程还是单线程,异步还是异步,callback还是callback。这一切都不会因为有一个Generator而有任何变化。