异步中的generatorasync

generator 内部多种状态。执行generator函数会返回一个遍历器。不仅是状态机,还 是遍历器生成函数,我们可以遍历每一个状态。于是又称之为生成器

async函数的实现原理,就是将generator函数和自动执行器,包装在一个函数里。

如何理解这句话,内部代码如何实现?是这篇文章的重点。

遍历器的 next

首先,generator函数会返回一个指向内部状态的指针对象,我们必须调用遍历器 的next方法才能走到generator函数内部的下一个状态。

于是,我们需要看一下next方法:

const Iterator = (arr) => {
    let nextIndex = 0
    return {
        next: function () {
            return nextIndex < arr.length
                ? { value: arr[nextIndex++] }
                : { done: true }
        },
    }
}
const demo = Iterator([1, 2, 3])
demo.next()

yield 与 *

回到generator本身,前面已经讲generator函数的调用并不会执行,而是必须用遍历器 对象的next方法。即每次调用next,内部指针就从接着从上次停下的地方继续执行,直 到遇到yield语句。

generator本身生成了遍历器,所以它的执行是分阶段用next不断触发的。yield表 达式是暂停执行标记,next则是继续执行。

我们可以在generator函数内很容易控制语句的执行与暂停,每一次的yield表达式都会 返回相应的value,基于此我们可以将异步执行放在yield中,暂停指针,当返 回value即有结果时,将value抛出再用next继续下一步。

function* Ge() {
    yield 'homo'
    yield 'bulla'
    return 'very Handsome'
}
let myself = Ge()
myself.next() // homo
myself.next() // bulla
myself.next() // very Handsome

上面的Ge函数通过调用next三次结束。返回一 个{value: "very Handsome", done: true},包含valuedone属性的对象 。value代表当前指针,done则表示遍历是否结束。

需要注意的是,yield是一个惰性求值语句,只有调用next时,才会执行其后面的语 句。

yield表达式本身没有返回值undefined,next方法可以带一个参数,该参数 就被当作上一个yield的返回值。这给我们提供了控制generator函数内部执行的一 个接口。

也就是说,generator函数从暂停状态到恢复运行。它的上下文状态是不变的

async

接下来是async,首先它和Generator是不同的。async会返回一个promise对象,在 一些复杂的异步场景,promiseallrace会得心应手。而Generator只是一个生 成器。

我们来看一下Generatorasync的使用:

// 使用 generator
var fetch = require('node-fetch')
var co = require('co')
function* gen() {
    var r1 = yield fetch('https://api.github.com/users/github')
    var json1 = yield r1.json()
    console.log(json1.bio)
}
co(gen)
// 使用 async
var fetch = require('node-fetch')
var fetchData = async function () {
    var r1 = await fetch('https://api.github.com/users/github')
    var json1 = await r1.json()
    console.log(json1.bio)
}
fetchData()

async函数就是将 Generator 函数的星号(*)替换成async,将yield替换 成await,并内置执行器。

进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象, 而await命令就是内部then命令的语法糖。