背景
    在学习async/await的时候,你肯定是会发现generator的字眼
    没错!
    async/await其实就是generator函数的语法糖

    参考大佬文章一边学习一边写的:

    https://juejin.cn/post/7007031572238958629

    学习
    看一下下面的例子:
    value表示执行的值,done表示当前的generator是否执行完毕
    根据例子来看,其实next函数调用,就代表执行力一下yield后面的值

    • 如果是常量,也就是是一个固定值,则next执行后的结果的value值对应的就是yield后面的值
    • 如果yield后面是一个函数,则next执行后value对应的就是函数的执行结果
    • 如果yield后面跟的是promise,那next执行后value对应的就是一个promise结果

    常量:

    1. function* gen() {
    2. yield 1
    3. yield 2
    4. yield 3
    5. }
    6. const g = gen();
    7. console.log(g.next()) // {value: 1, done: false}
    8. console.log(g.next()) // {value: 2, done: false}
    9. console.log(g.next()) // {value: 3, done: false}
    10. console.log(g.next()) // {value: undefined, done: true}

    有返回值:

    1. function* gen() {
    2. yield 1
    3. yield 2
    4. yield 3
    5. return 4
    6. }
    7. const g = gen();
    8. console.log(g.next()) // {value: 1, done: false}
    9. console.log(g.next()) // {value: 2, done: false}
    10. console.log(g.next()) // {value: 3, done: false}
    11. console.log(g.next()) // {value: 4, done: true}

    函数:

    1. function fn(val) {
    2. return val
    3. }
    4. function* gen() {
    5. yield fn(1)
    6. yield fn(2)
    7. yield fn(3)
    8. return 4
    9. }
    10. const g = gen();
    11. console.log(g.next()) // {value: 1, done: false}
    12. console.log(g.next()) // {value: 2, done: false}
    13. console.log(g.next()) // {value: 3, done: false}
    14. console.log(g.next()) // {value: 4, done: true}

    Promise:

    1. function fn(num) {
    2. return new Promise(resolve => {
    3. setTimeout(() => {
    4. resolve(num)
    5. }, 1000)
    6. })
    7. }
    8. function* gen() {
    9. yield fn(1)
    10. yield fn(2)
    11. return 3
    12. }
    13. const g = gen()
    14. console.log(g.next()) // { value: Promise { <pending> }, done: false }
    15. console.log(g.next()) // { value: Promise { <pending> }, done: false }
    16. console.log(g.next()) // { value: 3, done: true }

    next传参:

    • 第一次next传参是没用的,只有从第二次开始next传参才有用
    • next传值时,要记住顺序是,先右边yield,后左边接收参数

    这个地方可以多说一些

    1. 当第一次执行g.next(),执行yield 1
    2. 第二次执行的时候,执行console.log内容(内容就是第二次传入的参数),并且执行yield 2
    3. 第三次执行的时候,通2

    所以打印出来才会如下:第二次运行先进行打印,然后再执行

    1. function* gen() {
    2. const num1 = yield 1
    3. console.log(num1)
    4. const num2 = yield 2
    5. console.log(num2)
    6. return 3
    7. }
    8. const g = gen()
    9. console.log(g.next()) // { value: 1, done: false }
    10. console.log(g.next(11111))
    11. // 11111
    12. // { value: 2, done: false }
    13. console.log(g.next(22222))
    14. // 22222
    15. // { value: 3, done: true }

    实现async/await
    其实上方的generator函数的Promise+next传参,就很像async/await了,区别在于

    • gen函数执行返回值不是Promise,asyncFn执行返回值是Promise
    • gen函数需要执行相应的操作,才能等同于asyncFn的排队效果
    • gen函数执行的操作是不完善的,因为并不确定有几个yield,不确定会嵌套几次

    返回promise

    1. function* gen() {
    2. }
    3. function generatorToAsync (generatorFn) {
    4. return function () {
    5. return new Promise((resolve, reject) => {
    6. })
    7. }
    8. }
    9. const asyncFn = generatorToAsync(gen)
    10. console.log(asyncFn()) // Promise

    加入一系列复杂操作

    1. function fn(nums) {
    2. return new Promise(resolve => {
    3. setTimeout(() => {
    4. resolve(nums * 2)
    5. }, 1000)
    6. })
    7. }
    8. function* gen() {
    9. const num1 = yield fn(1)
    10. const num2 = yield fn(num1)
    11. const num3 = yield fn(num2)
    12. return num3
    13. }
    14. function generatorToAsync(generatorFn) {
    15. return function () {
    16. return new Promise((resolve, reject) => {
    17. const g = generatorFn()
    18. const next1 = g.next()
    19. next1.value.then(res1 => {
    20. const next2 = g.next(res1) // 传入上次的res1
    21. next2.value.then(res2 => {
    22. const next3 = g.next(res2) // 传入上次的res2
    23. next3.value.then(res3 => {
    24. // 传入上次的res3
    25. resolve(g.next(res3).value)
    26. })
    27. })
    28. })
    29. })
    30. }
    31. }
    32. const asyncFn = generatorToAsync(gen)
    33. asyncFn().then(res => console.log(res)) // 3秒后输出 8

    此时跟async/await就很像了,如下

    1. async function asyncFn() {
    2. const num1 = await fn(1)
    3. const num2 = await fn(num1)
    4. const num3 = await fn(num2)
    5. return num3
    6. }
    7. asyncFn().then(res => console.log(res)) // 3秒后输出 8

    上面的代码其实都是死代码,因为一个async函数中可能有2个await,3个await,5个await ,其实await的个数是不确定的。同样类比,generator函数中,也可能有2个yield,3个yield,5个yield,所以咱们得把代码写成活的才行

    1. function generatorToAsync(generatorFn) {
    2. return function() {
    3. const gen = generatorFn.apply(this, arguments) // gen有可能传参
    4. // 返回一个Promise
    5. return new Promise((resolve, reject) => {
    6. function go(key, arg) {
    7. let res
    8. try {
    9. res = gen[key](arg) // 这里有可能会执行返回reject状态的Promise
    10. } catch (error) {
    11. return reject(error) // 报错的话会走catch,直接reject
    12. }
    13. // 解构获得value和done
    14. const { value, done } = res
    15. if (done) {
    16. // 如果done为true,说明走完了,进行resolve(value)
    17. return resolve(value)
    18. } else {
    19. // 如果done为false,说明没走完,还得继续走
    20. // value有可能是:常量,Promise,Promise有可能是成功或者失败
    21. return Promise.resolve(value).then(val => go('next', val), err => go('throw', err))
    22. }
    23. }
    24. go("next") // 第一次执行
    25. })
    26. }
    27. }
    28. const asyncFn = generatorToAsync(gen)
    29. asyncFn().then(res => console.log(res))

    使用
    async/await版本使用

    1. async function asyncFn() {
    2. const num1 = await fn(1)
    3. console.log(num1) // 2
    4. const num2 = await fn(num1)
    5. console.log(num2) // 4
    6. const num3 = await fn(num2)
    7. console.log(num3) // 8
    8. return num3
    9. }
    10. const asyncRes = asyncFn()
    11. console.log(asyncRes) // Promise
    12. asyncRes.then(res => console.log(res)) // 8

    使用generator版本:

    1. function* gen() {
    2. const num1 = yield fn(1)
    3. console.log(num1) // 2
    4. const num2 = yield fn(num1)
    5. console.log(num2) // 4
    6. const num3 = yield fn(num2)
    7. console.log(num3) // 8
    8. return num3
    9. }
    10. const genToAsync = generatorToAsync(gen)
    11. const asyncRes = genToAsync()
    12. console.log(asyncRes) // Promise
    13. asyncRes.then(res => console.log(res)) // 8