😁深入JS(四): 一文让你完全了解Iterator+Generator 实现async await

1、什么是Iterator

1.1、Iterator作用

Iterator 的作用有三个:

  • 为各种数据结构,提供一个统一的、简便的访问接口
  • ES6 创造了一种新的遍历命令for..of循环,Iterator 接口主要供for..of消费

1.2、什么数据结构能被称为iterator

一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口;

Symbol.iterator规则

  • 内部必须实现 next() 方法
  • 迭代器的 next() 方法:
    • 必须返回一个具有 done 和 value 属性的对象。
    • done(布尔值):表示迭代是否结束。
    • value:当前迭代的值(done 为 true 时可省略)。

1.3、内置iterator的数据结构

  1. 字符串
  2. 数组
  3. Map
  4. Set
  5. arguments 对象
  6. NodeList 等 DOM 集合类型

1.4、iterator的数据结构特性

  1. for-of 循环
  2. 数组解构
  3. 扩展操作符
  4. Array.from() 参数传入一个类数组或者迭代器
  5. 创建 Set
  6. 创建 Map
  7. Promise.all()接收由 Promise组成的可迭代对象
  8. Promise.race()接收由 Promise组成的可迭代对象
  9. yield*操作符,在生成器中使用

1.5、手写Symbol.iterator实现解构与扩展操作

手写symbol.iterator

js 复制代码
let obj = {
  name: "tom",
  age: 18,
  gender: "男",
  intro: function () {
    console.log("my name is " + this.name);
  },
  [Symbol.iterator]: function () {
    let i = 0;
    // 获取当前对象的所有属性并形成一个数组
    let keys = Object.keys(this);
    return {
      next: function () {
        return {
          // 外部每次执行next都能得到数组中的第i个元素
          value: keys[i++],
          // 如果数组的数据已经遍历完则返回true
          done: i > keys.length,
        };
      },
    };
  },
};
let [a, b] = obj;
console.log(a); //name
console.log(b); //age
console.log([...obj]);//[ 'name', 'age', 'gender', 'intro' ]

盗用symbol.iterator

通过Object.keys或者Object.values将对象转换为数组,盗用数组的iterator

js 复制代码
let obj = {
  name: "tom",
  age: 18,
  gender: "男",
  intro: function () {
    console.log("my name is " + this.name);
  },
};
obj[Symbol.iterator] = function () {
  let arr = Object.keys(this);
  return arr[Symbol.iterator]();
};
let [a, b] = obj;
console.log(a); //name
console.log(b); //age
console.log([...obj]); //[ 'name', 'age', 'gender', 'intro' ]

2、Generator

普通对象的 Iterator ,开发人员还需手动调用next(),获取value并判断done遍历是否中断
于是 Generator 函数就应运而生了,它被设计出来的目的就是:简化Iterator函数的编写。

js 复制代码
let obj = {
  name: "tom",
  age: 18,
  gender: "男",
  intro: function () {
    console.log("my name is " + this.name);
  },
  // 使用 Generator 函数实现迭代器
  [Symbol.iterator]: function* () {
    // 获取当前对象的所有属性并形成一个数组
    let keys = Object.keys(this);
    // 使用 for...of 循环遍历 keys 数组并逐个 yield 每个属性名
    for (let key of keys) {
      yield key;
    }
  },
};

3.1、利用generator实现链式网络请求

js 复制代码
function request(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(url);
    }, 2000);
  });
}  
function* getData() {
  const res1 = yield requestData("why")
  console.log("res1:", res1)

  const res2 = yield requestData(res1 + "kobe")
  console.log("res2:", res2)

  const res3 = yield requestData(res2 + "james")
  console.log("res3:", res3)
}
const generator = getData()
  generator.next().value.then(res1 => {
      generator.next(res1).value.then(res2 => {
        generator.next(res2).value.then(res3 => {
          generator.next(res3)
        })
      })
    })

3.Iterator+Generator 实现async await

Generator 生成器函数的特点就是卡顿

每次运行到 yield 语句时就会"卡住",需要调用其返回的 Iterator 对象的 next 方法之后,才会继续往下执行,然后又在下一次 yield 处"卡住"。

以此我们可以自己手动实现一个递归函数:

  • 接受一个生成器函数
  • 内部实现一个递归函数
  • 递归函数内部执行next()方法
  • 判断 done 属性:
  • done 为 true:结束
  • done 为 false:
    1. value是一个Promise
    2. 调用 promise.then 方法,等待异步操作完成。取出其兑现值,进行递归,并将兑现值作为参数传入
js 复制代码
function request(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(url);
    }, 2000);
  });
}
function* getData() {
  const res1 = yield request("1");
  console.log("res1:", res1);

  const res2 = yield request(res1 + "2");
  console.log("res2:", res2);

  const res3 = yield request(res2 + "3");
  console.log("res3:", res3);
}
function execGenFn(fn) {
  // 1.获取对应函数的generator
  const gen = fn();
  // 2.定义一个递归函数
  function exec(res) {
    // result -> { done: true/false, value: 值/undefined }
    const result = gen.next(res);
    if (result.done) return;
    result.value.then((res) => {
      exec(res);
    });
  }
  // 3.执行递归函数
  exec();
}
execGenFn(getData);
相关推荐
沐言人生2 分钟前
82.4k 星!把十几万行代码变成知识图谱,新人终于不用硬啃了
前端·后端·github
溪语流沙2 分钟前
【Web全栈进阶】JWT无状态认证:签发、校验、刷新
前端·git·python·github
在繁华处7 分钟前
2.1 上下文:决定 Agent 能力上限的关键
前端·人工智能·microsoft
ndsc_d8 分钟前
2026年有哪些好用的AI UI设计工具?主流工具功能和适用场景对比
前端·人工智能·ui·ai·设计师·ai ui·ai ui工具
溪语流沙30 分钟前
【每天一个CSS | Day18】会摆动的拟物怀表:黄铜、皮革与催眠摆
前端·css3·html5
Lvan的前端笔记36 分钟前
docker:基于docker的前端部署方案
前端·docker·状态模式
程序人生88836 分钟前
从“把隐私文件上传到别人云端“到本地可控:DocConverter Web 立项初心与架构选型
前端·图像处理·人工智能·opencv·架构·ocr·文心一言
开开心心就好44 分钟前
视频里的图片怎么提取?双击一下就导出
java·前端·人工智能·spring·智能手机·intellij-idea·excel
爱喝水的小周1 小时前
实验十 Vue‑Router 王者荣耀战绩查询 APP 开发实践
前端·javascript·vue.js
yexianglunbai1 小时前
Vue 3 全面详解:从核心特性到工程化实践
前端·javascript·vue.js