侧边栏壁纸
博主头像
woku博主等级

成功的路上并不拥挤

  • 累计撰写 50 篇文章
  • 累计创建 13 个标签
  • 累计收到 3 条评论

你是否有了解 async+await实现 ?

woku
2022-06-16 / 2 评论 / 3 点赞 / 503 阅读 / 5,739 字

generator和Iterator

generator

generator:生成器函数,简单理解就是可以生成一个东西
和一般函数的不同之处在于:
function关键字与函数名之间有一个星号

function * generator() {}

函数体内部使用yield表达式,定义不同的内部状态

   function* generator() {
         console.log('我执行啦~~~')
         yield 'a';
         yield 'b';
         return 'c';
   }

调用generator后,并不会执行,返回的也不是函数运行的结果,返回的是一个遍历器对象(Iterator Object)- 内部状态的指针对象

var it = generator()
it.next()
it.next()
it.next()
it.next()

通过调用it的next方法,函数generator才会被执行
第一次调用,函数执行,到遇到第一个yield表达式为止,不再往下执行。next方法返回一个对象{value:yield表达式的值,done:当前遍历是否结束}
第二次调用,函数从上一次yield表达式停下的地方开始执行,直到遇到下一个yield表达式停止,同样返回一个包含value和done的对象
第三次调用,函数从上一次yield表达式停下的地方开始执行,后面没有遇到新的yield表达式,那么就一直执行,直到retrun或者函数执行完毕停止。如果return了值,返回的对象中的value就是这个return的值,如果没有return,那么默认的return的是undefined,done为true。

iterator

生成器函数执行完后,会返回一个迭代器对象
这个对象中有一个next方法
执行next方法返回一个迭代器对象,包含value 值done迭代是否结束

对数组的遍历可控

    function* gen(arr) {
        for (var i = 0; i < arr.length; i++) {
            yield arr[i]
        }
    }
    var iterator = gen([1, 2, 3, 4)
    console.log(iterator.next())
    console.log(iterator.next())
    console.log(iterator.next())
    console.log(iterator.next())
    console.log(iterator.next())

1637025618678.png

模拟生成器函数

function gen(arr) {
    let nextIndex = 0
    return {
        next: function() {
           return nextIndex < arr.length - 1 ? {
              value: arr[nextIndex++],
              done: false
           } : {
              value: arr[nextIndex++] || undefined,
              done: true
           }
        }
    }
}
let iterator = gen([1, 2, 3])
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())

babel编译generator

babel将ES6中的generator编译为ES5代码
es6:

function * gen() {
    yield 1
    yield 2
    yield 3
    return 4
}

bable编译后:

注意是编译后的代码,不是实现的源码喔!
通过switch-case来完成迭代的过程

function gen$(ctx) {
    while (true) {
        switch (ctx.current = ctx.next) {
            case 0:
                ctx.next = 1
                return '1'
            case 1: 
                ctx.next = 2
                return '2'
            case 2:
                ctx.next = 3
                return '3'
            case 3:
                ctx.finish()
                return '4'
        }
    }
}

function gen() {
    const ctx = {
        current: 0,
        next: 0,
        done: false,
        finish() {
            this.done = true
        }
    }
    return {
        next: function() {
            return {
                value: gen$(ctx),
                done: ctx.done
            }
        }
    }
}
const genFn = gen()

console.log(genFn.next())
console.log(genFn.next())
console.log(genFn.next())
console.log(genFn.next())

generator完成文件读取

需求场景

分别有user.json和class.json两个文件

[{
  "id": "1",
  "name": "张三"
},
 {
   "id": "2",
   "name": "李四"
 },
 {
   "id": "3",
   "name": "王五"
 }
]
[{
        "id": "1",
        "name": "前端",
        "students": "[1, 2]"
    },
    {
        "id": "2",
        "name": "后端",
        "students": "[1, 3]"
    },
    {
        "id": "3",
        "name": "大数据",
        "students": "[2, 3]"
    }
]

通过学生id查询,返回该学生的id,name和所有的class集合(如下结构)

{
  "id": "1",
  "name": "张三",
  "classes": [{
    "id: "1",
    "name": "前端"
  },{
    "id: "2",
    "name": "后端"
  }]
}

generator函数 + yield

  • 使用fs模块读取文件内容(promise化)
  • generator函数 + yield
const fs = require('fs').promises
function * getUserClasses(uid) {
    // 返回的是一个promise
    let userDatas = yield fs.readFile('./data/user.json', 'utf-8')
    userDatas = JSON.parse(userDatas)
    let userData = userDatas.find(x => x.id == uid)
    let classDatas = yield fs.readFile('./data/class.json', 'utf-8')
    classDatas = JSON.parse(classDatas)
    let userClassData = {
        id: userData.id,
        name: userData.name,
        classes: []
    }
    classDatas.map(x => {
        const students = JSON.parse(x.students)
        if (students.includes(uid)) {
            userClassData.classes.push({
                id: x.id,
                name: x.name
            })
        }
    })
    return userClassData
}
module.exports = {
    getUserClasses
}
  • 调用getUserClasses生成器函数,得到迭代器对象
const uid = 1
const it = getUserClasses(uid)
const { value, done } = it.next()
console.log(value) // Promise { <pending> }

当第一次next时,getUserClasses生成器执行到第一个yield停止。此时value是一个Promise对象

  • 使用.then来拿结果
const uid = 1
const it = getUserClasses(uid)
const { value, done } = it.next()
value.then(res => {
    console.log(res)
})

res是第一次读取user.json文件的结果

  • 继续next,进入到下一个yield
const uid = 1
const it = getUserClasses(uid)
const { value, done } = it.next()
value.then(res => {
    const { value, done } = it.next(res)
    console.log(value)
})
// it.next(res)  res是上一次yield的返回值,也就是userDatas的值

到达第二个yield,停止,得到value和done。 value同样是一个Promise。

  • 使用.then来拿结果
const uid = 1
const it = getUserClasses(uid)
const { value, done } = it.next()
value.then(res => {
    const { value, done } = it.next(res)
    value.then(res => {
        const { value, done } = it.next(res)
        console.log(value)
    })
})

最后一次的value是return的值(后面没有yield了)

需要多次的调用then来获取结果,形成多次嵌套
可以用一层就拿到结果吗?类似下面这种。

getUserClasses(uid).then(res => {
  console.log(res)
},err => {
  console.log(err)
})

异步迭代函数

使用co函数 -> 异步迭代函数

function co (iterator) {
    return new Promise((resolve, reject) => {
        // 迭代器递归函数,参数,传给next的值
        function walk(data) {
            const { value, done } = iterator.next(data)
            if (!done) {
                Promise.resolve(value).then(res => {
                    walk(res)
                }, e => {
                    reject(e)
                })
            } else {
                resolve(value)
            }
        }
        walk()
    })
}
module.exports = {
    co,
    getUserClasses
}
const { getUserClasses, co }= require('./getUserClasses')
const uid = 1
const it = getUserClasses(uid)
co(it).then(res => {
    console.log(res)
}, err => {
    console.log(err)
})

async+await代替yield

async function getUserClasses(uid) {
    let userDatas = await fs.readFile('./data/user.json', 'utf-8')
    userDatas = JSON.parse(userDatas)
    let userData = userDatas.find(x => x.id == uid)
    let classDatas = await fs.readFile('./data/class.json', 'utf-8')
    classDatas = JSON.parse(classDatas)
    let userClassData = {
        id: userData.id,
        name: userData.name,
        classes: []
    }
    classDatas.map(x => {
        const students = JSON.parse(x.students)
        if (students.includes(uid)) {
            userClassData.classes.push({
                id: x.id,
                name: x.name
            })
        }
    })
    return userClassData
}

使用的时候,就很方便了

const { getUserClasses } = require('./getUserClasses')

const uid = 1
const it = getUserClasses(uid)
it.then(res => {
    console.log(res)
}, err => {
    console.log(err)
})

generator + yield + co === async + await (语法糖)

async+await使用场景

通过用于异步ajax请求,文件读取

function getData() {
  axios({
    url: 'xxx',
    data: 'xxx'
  }).then(res => {
    return 'xxx'
  }
}
          
async mounted() {
    const data = await getData()
}
async created() {
  const userInfo = await this.getUserInfo()
  const listData = await this.getListData()
}
methods: {
  getUserInfo() {
    retrun new Promise((resolve, reject) => {
      ajax({
      }).then(res => {
        resolve(res)
      }
    })
  },
  getListData() {
   retrun new Promise((resolve, reject) => {
      ajax({
      }).then(res => {
        resolve(res)
      }
    })
  }
}
3

评论区