细说JavaScript中的generator

在JavaScript中的generator可以帮助我们解决什么问题? generator可以解决异步编程问题,生成迭代器,可以让我们异步代码,写的像同步,一个通用点的说法是 * + yield = async + await的形式 让我们往下看,它的具体用法:

  1. generator可以将一个函数分成多段执行,碰到yield的就停止了,此时yield的结果会作为value值,具体代码如下:
JavaScript 复制代码
function* fn() {
  yield 'hello'
  yield '周一'
  return 50
}
let iterator = fn()
console.log(iterator.next())  // 输出 { value: 'hello', done: false }
console.log(iterator.next()) // 输出 { value: '周一', done: false }
console.log(iterator.next())  // 输出 { value: 100, done: true }
console.log(iterator.next()) // 输出 { value: undefined, done: true }

应用场景: 类数组 转化为 数组 那么什么是类数组? 有索引,有长度,可以迭代 看如下代码

JavaScript 复制代码
let likeArray = {
  0: 1,
  1: 2,
  2: 3,
  3: 4,
  length: 4,
}

// 迭代数组中每一项,将每一项放到新数组中
let arr = [...likeArray] // ...这个操作会调用迭代器,但是类数组里面缺少
console.log(arr)

上面代码会出错,显示 likeArray is not iterable,因为它是类数组,缺少迭代器,不能用展开运算符,具体代码,可以改成iterator。

JavaScript 复制代码
写法一: 
let likeArray = {
  0: 1,
  1: 2,
  2: 3,
  3: 4,
  length: 4,
  [Symbol.iterator]() {
    let i = 0
    return {
      next: () => {
        return { value: this[i], done: this.length === i++}
      }
    }
  }
}
let arr = [...likeArray]
console.log(arr) // [ 1, 2, 3, 4 ]
写法二: 
// 迭代的时候会自动调用next
let likeArray = {
  0: 1,
  1: 2,
  2: 3,
  3: 4,
  length: 4,
  [Symbol.iterator]: function *(){ // 迭代的时候会自动调用next
    let i = 0
    let len = this.length
    while(len !== i) {
      yield this[i++]
    }
  }
}
let arr = [...likeArray]
console.log(arr) // [ 1, 2, 3, 4 ]

那么,我们可以利用generator的机制,每次调用next之后,都可以做一些处理,再去执行下一步 .then.then

JavaScript 复制代码
function* fn() {
  let r1 = yield 'hello'
  console.log(r1)
  let r2 = yield 'world'
  console.log(r2)
  // return 100
}

let iterator = fn()
console.log(iterator.next())
console.log(iterator.next('abc')) // 调用next方法的时候,可以给上一次yield的返回值赋值 r1 abc
console.log(iterator.next('cde')) // 调用next方法的时候,可以给上一次yield的返回值赋值 r2 cde

// 注意 iterator.throw() 可以被try catch 给捕获到

generator+promise怎么使用? 看一个实战案例:

a.txt -> b b.txt -> bbbbb

JavaScript 复制代码
const fs = require('fs/promises')
const path = require('path')
function* readFile() {
  let data1 = yield fs.readFile(path.resolve(__dirname, 'a.txt'), 'utf8')
  let data2 = yield fs.readFile(path.resolve(__dirname, data1), 'utf8')
  return data2
}

let it = readFile()
let { value, done } = it.next()
value.then(data1 => {
  let { value, done } = it.next(data1 + '.txt')
  value.then(data2 => {
    let { value, done } = it.next(data2)
    console.log(value) // bbbbb
  })
})

上面的递归很麻烦,我们可以采用co库来进行改造,具体代码如下:

npm install co

JavaScript 复制代码
const fs = require('fs/promises')
const path = require('path')
function* readFile() {
  let data1 = yield fs.readFile(path.resolve(__dirname, 'a.txt'), 'utf8')
  let data2 = yield fs.readFile(path.resolve(__dirname, data1+'.txt'), 'utf8')
  return data2
}
const co = require('co')
co(readFile()).then(data => {
  console.log(data)
})

几行代码就搞定了,那么它是怎么做到的呢,我们先来解剖一下

JavaScript 复制代码
function co(it) {
  return new Promise((resolve, reject) => {
    function setp(data) {
      let { value, done } = it.next(data)
      if(!done) {
        Promise.resolve(value).then(data => { // 第一步完成
          setp(data) // 下一步
        }).catch(e => {
          reject(e)
        })
      } else {
        resolve(value)
      }
    }
    setp()
  })
}

co(readFile()).then(data => {
  console.log(data)
}).catch(e => {
  console.log(e)
})
相关推荐
FungLeo8 小时前
成为全栈·React 管理后台篇·总复盘:从“接口能调通”到“后台值得使用”
前端·react.js·前端框架·项目复盘·成为全栈
Csvn9 小时前
并发模式:让渲染学会排队、插队和让路
前端
可乐鸡翅yeah_9 小时前
新手梳理:M3U8 线上问题,哪些是前端锅,哪些是后端锅
前端·ios·音视频·实时音视频·m3u8·音视频在线播放
Flynt9 小时前
Linear 用 1000 个 PR 换掉 styled-components,我写了 200 个按钮,把这笔账复现了一遍
前端·css·preact
JavaGuide10 小时前
NVIDIA 又开源了!这次给 AI Agent 加上权限管控
前端·后端
excel10 小时前
prisma 如何处理数据库竞态
前端·数据库·后端
莪_幻尘12 小时前
Skill 体检:30 个 Skill 全凭感觉?体检器先自曝了 8 个“假 0 分
前端·人工智能·llm
风骏时光牛马12 小时前
AI模型综合能力评测:性能、指令遵循与多场景实测对比
前端
Frag0ut12 小时前
Chrome与Chromium内核浏览器在Windows 11上的新特性全景解析
前端·chrome·windows·web安全·chromium·gemini ai·playready drm
hiahiahia12312 小时前
实现完整 Tool Dispatcher
开发语言·前端