Skip to main content

3. generator

Promise 通过链式调用解决了异步问题 .then(()=>{}).then().then() 还是基于回调来实现

1.generator 概念

基于 promise 把异步代码变得更像同步

  • generator 是一个特殊的函数 (生成器) 这个函数可以暂停,也可以继续执行

  • 可以通过 generator 来控制我们异步流程( 类似于调试的 debugger)

function* read() {
// es6语法
// 产出
yield "vue"
yield "react"
return "node"
}

所谓的生成器 执行后返回的是迭代器,当我调用迭代器的时候 就可以向下继续执行

let it = read() // iterator 迭代器, 迭代器必须要拥有一个next方法
console.log("it=>", it)
// next方法 {value:表示当前产出的值,done:是否函数执行完成}
console.dir(it.next()) //{ value: 'vue', done: false }
console.log(it.next()) //{ value: 'react', done: false }
console.log(it.next()) //{ value: 'node', done: true }
console.log(it.next()) //{ value: 'undefined', done: true }

现在是手动调用,正常应该自动帮我们调用 next

2.generator 使用场景

  • 遍历的时候 就可以使用 generator 来进行实现

返回 new Set([1, 2, 3]) 的第一项

let set = new Set([1, 2, 3])
// set 调用.values()返回的就是迭代器
console.log(set.values().next().value)
console.log([...set][0])
  • 将一个类数组转换成数组

类数组:有索引、有长度、能遍历 就是类数组

const likeArray = { 0: 1, 1: 2, 2: 3, length: 3 } // 只有索引和长度如何遍历
console.log(Array.from(likeArray)) // [ 1, 2, 3 ] from 内部调用了generator
for (let key of likeArray) {
} // TypeError:likeArray is not iterable
const arr = [...likeArray] // TypeError:likeArray is not iterable

实现 Symbol.iterator

const likeArray = { 0: 1, 1: 2, 2: 3, length: 3 }
// Symbol 有很多操作可以改变原有的js的特性,元编程
likeArray[Symbol.iterator] = function () {
let arr = this // -> likeArray
let len = arr.length
let idx = 0
return {
next() {
// {value:ok,done:true}
return { value: arr[idx], done: idx++ === len }
},
}
}

const arr = [...likeArray]
console.log(arr) // [ 1, 2, 3 ]

generator 函数返回一个 iterator 迭代器

const likeArray = { 0: 1, 1: 2, 2: 3, length: 3 }
likeArray[Symbol.iterator] = function* () {
let len = this.length
let idx = 0
while (idx !== len) {
yield this[idx++]
}
}
const arr = [...likeArray]
console.log(arr) // [ 1, 2, 3 ]

3. yield 返回值

  • 无参数情况
function* read() {
let a = yield "vue"
console.log(a, "a")
let b = yield "react"
console.log(b, "b")
return "node"
}
const it = read()
it.next()
it.next() // undefined a
it.next() // undefined b
  • next 传参

特点是 next 中传递的参数是上一次 yield 的返回值,但是第一的 next 参数是无意义的

function* read() {
try {
let a = yield "vue"
console.log(a, "a") // 我处理第一个异步逻辑出错了,就不要继续执行了
let b = yield "react"
console.log(b, "b")
return "node"
} catch (e) {
console.log(e, "出错了")
}
}
const it = read()
it.next("a") // 第一的next参数是无意义的
it.next("abc") // abc a //next中传递的参数是上一次yield的返回值
it.next("bcd") // bcd b

每次 next 传的参数都是给上一次 yield 的返回值

  • 抛错
function* read() {
let a = yield "vue"
console.log(a, "a") // 我处理第一个异步逻辑出错了,就不要继续执行了
let b = yield "react"
console.log(b, "b")
return "node"
}
const it = read()
it.next("a") // 第一的next参数是无意义的
it.next("abc") // abc a //next中传递的参数是上一次yield的返回值
it.throw("出错了") // 出错了 出错了
it.next("bcd")

4. 案例(读取文件)

  • 最早 callback 来解决异步

  • promise 链式调用

  • generator + promise + co

  • async + await

fileUrl.txt

name.txt

name.txt

Jiang

1. generator

main.js

const fs = require("fs/promises")
const path = require("path")
function* readFile() {
// 基于generator
try {
console.log(
'path.resolve(__dirname, "fileUrl.txt")=>',
path.resolve(__dirname, "fileUrl.txt")
)
let data = yield fs.readFile(path.resolve(__dirname, "fileUrl.txt"), "utf8")
let name = yield fs.readFile(path.resolve(__dirname, "name.txt"), "utf8")
return name
} catch (e) {
console.log(e, "读取出错")
}
}
const it = readFile()
{
let { value, done } = it.next()
Promise.resolve(value).then((data) => {
// data 就是第一次产出后的promise结果
let { value, done } = it.next(data)
Promise.resolve(value).then((name) => {
let { value, done } = it.next(name)
console.log(value, done)
})
})
}

2. co 库(generator + promise + co)

const fs = require("fs/promises")
const path = require("path")
const co = require("co")
function* readFile() {
// 基于generator
try {
console.log(
'path.resolve(__dirname, "fileUrl.txt")=>',
path.resolve(__dirname, "fileUrl.txt")
)
let data = yield fs.readFile(path.resolve(__dirname, "fileUrl.txt"), "utf8")
let name = yield fs.readFile(path.resolve(__dirname, data), "utf8")
return name
} catch (e) {
console.log(e, "读取出错")
}
}
co(readFile()).then((data) => {
console.log(data)
})

3. co 库(generator + promise + co)

const fs = require("fs/promises")
const path = require("path")
function co(it) {
return new Promise((resolve, reject) => {
// 递归回调
function next(data) {
// 异步递归处理
let { value, done } = it.next(data)
if (!done) {
Promise.resolve(value).then((data) => {
next(data)
})
} else {
resolve(value) // 整个geneator执行完毕了 结束
}
}
next() // koa express
})
}

function* readFile() {
// 基于generator
try {
console.log(
'path.resolve(__dirname, "fileUrl.txt")=>',
path.resolve(__dirname, "fileUrl.txt")
)
let data = yield fs.readFile(path.resolve(__dirname, "fileUrl.txt"), "utf8")
let name = yield fs.readFile(path.resolve(__dirname, data), "utf8")
return name
} catch (e) {
console.log(e, "读取出错")
}
}
co(readFile()).then((data) => {
console.log(data)
})

4. async 和 await

// (async + await === generator + co) + promise的

const fs = require("fs/promises")
const path = require("path")
async function readFile() {
// 基于generator
try {
let data = yield fs.readFile(path.resolve(__dirname, "fileUrl.txt"), "utf8")
let name = await fs.readFile(path.resolve(__dirname, data), "utf8")
// await Promise.all()
// await Promise
return name
} catch (e) {
console.log(e, "读取出错")
}
}
readFile().then((data) => {
console.log(data)
})