Javascript:迭代器和生成器原理及异步应用

55 阅读3分钟

可迭代对象

是什么

可以使用for of遍历的对象。对象内部包含一个返回迭代器(一个对象)的函数,迭代器通过实现next方法来遍历对象的属性。

实现的原理:迭代器

  1. 实现[Symblo.iterator]方法
  2. 该方法返回一个迭代器(iterator)
  3. 迭代器实现一个next方法
  4. next() 方法,返回一个包含两个属性的对象:{value: 迭代的当前值, done: 一个布尔值,指示迭代是否完成}

手写原生迭代对象

const infos = { 
  name: "why",
  age: 18,
  height: 1.88,
  [Symbol.iterator]: function() {
    const entries = Object.entries(this)
    let index = 0
    // 这是一个迭代器
    const iterator = { 
      next: function() { 
        if (index < entries.length) {
          return { done: false, value: entries[index++] } 
        } else { 
            return { done: true } 
          } 
      } 
    }
    return iterator
  }
}

遍历方式一:使用迭代器的next方法

const iterator = infos[Symbol.iterator]() 
console.log(iterator.next()) // {done: false, value: ['name', 'why']} 
console.log(iterator.next()) // {done: false, value: ['age', ’18']}
console.log(iterator.next()) // {done: false, value: ['height', '1.88']}
console.log(iterator.next()) // {done: ture}

遍历方式二:for of

for (const item of infos) {
  const [key, value] = item
  console.log(key, value)
}

手写生成可迭代对象的构造函数


function Person(name, age, height, friends) {
  this.name = name;
  this.age = age;
  this.height = height;
  this.friends = friends;

  // 实例方法
  // this.running = function() {}

  this[Symbol.iterator] = function() {
    let index = 0;
   //这是一个迭代器
    const iterator = {
      next: () => {
        if (index < this.friends.length) {
          return { done: false, value: this.friends[index++] };
        } else {
          return { done: true };
        }
      }
    };
    return iterator;
  };
}

const p1 = new Person("why", 18, 1.88, ["curry", "kobe", "james", "tatumu"]);
const p2 = new Person("kobe", 30, 1.98, ["curry", "james", "aonier", "weide"]);

for (const item of p2) {
  console.log(item);
}

js中内置的可迭代对象

  • 数组: 实现了 Symbol.iterator,可以使用 for...of 遍历。
  • 字符串: 字符串也是可迭代对象,允许逐个字符访问。
  • Map 和 Set: 这两种集合类型也实现了迭代协议。

生成器

是什么

一个函数,本质上是[Symbol.iterator] () {}。调用该函数时,返回一个迭代器。该迭代器遍历的值为yield表达式。

如何使用

function* generatorFunction() {
  yield '第一步';
  yield '第二步';
  yield '第三步';
}
const gen = generatorFunction();
console.log(gen.next()); // { value: '第一步', done: false }
console.log(gen.next()); // { value: '第二步', done: false }
console.log(gen.next()); // { value: '第三步', done: false }
console.log(gen.next()); // { value: undefined, done: true }

应用:实现斐波那契数列

function* fibonacci() {
  let a = 0, b = 1;
  while (true) {
    yield a; // 生成当前值
    [a, b] = [b, a + b]; // 更新到下一个值
  }
}

const fib = fibonacci();

console.log(fib.next().value); // 0
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2
console.log(fib.next().value); // 3
console.log(fib.next().value); // 5

应用

  1. 处理流式数据(斐波那契数列)
  2. 循环控制
  3. 异步

异步的应用

promise + 生成器结合使用

实例

// 模拟的异步操作:验证用户信息
function validateUser(user) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (user.username && user.password) {
        resolve(user);
      } else {
        reject(new Error());
      }
    }, 1000);
  });
}
// 模拟的异步操作:保存用户信息
function saveUser(user) {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("用户信息已保存:", user);
      resolve(user);
    }, 1000);
  });
}
// 模拟的异步操作:发送欢迎邮件
function sendWelcomeEmail(user) {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(`欢迎邮件已发送给: ${user.username}`);
      resolve();
    }, 1000);
  });
}
// 生成器函数
function* registerUser(user) {
  try {
    const validatedUser = yield validateUser(user);
    const savedUser = yield saveUser(validatedUser);
    yield sendWelcomeEmail(savedUser);
    console.log("注册流程完成");
  } catch (error) {
    console.error(error.message);
  }
}
// 执行生成器
function run(generator, user) {
  const gen = generator(user);

  function handle(result) {
    if (result.done) return; // 结束

    // result.value 是一个 Promise对象
    result.value
      .then((value) => {
        handle(gen.next(value)); // 将结果传递给下一个 yield
      })
      .catch((error) => {
        gen.throw(error); // 捕获错误
      });
  }
  handle(gen.next()); // 启动生成器
}

// 示例用户
const newUser = {
  username: "exampleUser",
  password: "password123",
};

// 启动注册流程
run(registerUser, newUser);

但是,这样仍然复杂,可以使用async/await进一步优化。

// 使用 async 函数处理注册流程
async function registerUser(user) {
  try {
    const validatedUser = await validateUser(user);
    const savedUser = await saveUser(validatedUser);
    await sendWelcomeEmail(savedUser);
    console.log("注册流程完成");
  } catch (error) {
    console.error(error.message);
  }
}

// 示例用户
const newUser = {
  username: "exampleUser",
  password: "password123",
};

// 启动注册流程
registerUser(newUser);

对比C++迭代器的实现原理

C++ 迭代器实现原理

C++ 迭代器的实现主要基于指针抽象,依赖于存储的内存是连续的。本质上是对指针的一种封装,提供一系列操作。比如,在 std::vector 中,迭代器的实现通常只是元素数组的一个指针,递增迭代器就等同于移动指针到数组的下一个元素位置。++it 实际上是指针自增,直接指向内存中下一个元素的地址。

js迭代器实现原理

JavaScript 的迭代器基于迭代协议。其实现不依赖底层内存地址,而是通过对象和方法来控制迭代过程。