理解 JavaScript 中的迭代器、可迭代对象、生成器以及异步迭代
在现代 JavaScript 开发中,迭代器和可迭代对象是非常重要的概念,它们为我们提供了一种统一的方式来遍历数据结构。与此相关的是生成器,它们使得我们可以更灵活地控制函数的执行。
一、迭代器和可迭代对象
1.1 可迭代对象
在 JavaScript 中,任何实现了 Symbol.iterator 方法的对象都被称为可迭代对象。常见的可迭代对象包括数组、字符串、Map、Set 等。可迭代对象可以使用 for-of 循环直接遍历。
const arr = [1, 2, 3];
for (const value of arr) {
console.log(value); // 输出 1, 2, 3
}
1.2 迭代器
迭代器是一个实现了 next() 方法的对象,next() 方法返回一个包含两个属性的对象:
value: 返回当前迭代的值done: 一个布尔值,表示迭代是否完成
我们可以手动创建一个迭代器:
function createIterator(array) {
let index = 0;
return {
next() {
if (index < array.length) {
return { value: array[index++], done: false };
} else {
return { done: true };
}
}
};
}
const iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { done: true }
二、生成器
生成器是特殊类型的函数,可以通过 function* 关键字定义。生成器函数返回一个迭代器对象,调用 next() 方法会执行生成器函数,直到遇到 yield 语句。
function* generator() {
yield 1;
yield 2;
yield 3;
}
const gen = generator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { done: true }
生成器让我们能够在函数执行过程中暂停和恢复状态,适用于处理异步数据流和复杂迭代逻辑。
三、使用 for-of 循环
for-of 循环可以用来遍历任何可迭代对象,包括数组、字符串、Set 和 Map。其语法简单优雅,使得对可迭代对象的遍历变得直观。
const iterable = [10, 20, 30];
for (const value of iterable) {
console.log(value); // 输出 10, 20, 30
}
3.1 在对象中使用 for-of
为了使对象可用于 for-of 循环,我们需要为其实现 Symbol.iterator 方法。
const obj = {
values: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
return {
next: () => ({
value: this.values[index++],
done: index > this.values.length
})
}
}
};
for (const value of obj) {
console.log(value); // 输出 1, 2, 3
}
// 也可以通过调用迭代器手动遍历
// const iterator = obj[Symbol.iterator]();
// let result = iterator.next();
// while (!result.done) {
// console.log(result.value);
// result = iterator.next();
// }
四、异步迭代:for-await-of
在处理异步数据流(如网络请求、数据库查询等)时,for-await-of 是一种非常实用的语法。它允许我们等待异步迭代器的每一项。
4.1 创建异步迭代器
async function* asyncGenerator() {
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
for (let i = 1; i <= 3; i++) {
await delay(1000); // 每次等待 1 秒
yield i;
}
}
(async () => {
for await (const value of asyncGenerator()) {
console.log(value); // 输出 1, 2, 3 (每次间隔 1 秒)
}
})();
五、结合 Promise 和异步迭代
异步迭代器在处理 Promise 时特别有用。它们使得以更可读的方式处理多个异步操作变得更容易。
5.1 示例:处理异步 Promise
const fetchData = (id) => {
return new Promise((resolve) => {
setTimeout(() => resolve(`Data ${id}`), 1000);
});
};
async function* fetchAsyncData() {
for (let id = 1; id <= 3; id++) {
yield fetchData(id);
}
}
(async () => {
for await (const data of fetchAsyncData()) {
console.log(data); // 输出 Data 1, Data 2, Data 3 (每次间隔 1 秒)
}
})();
结论
通过理解迭代器、可迭代对象、生成器以及异步迭代,我们能够更灵活和高效地处理数据集。迭代器提供了一种统一的遍历接口,而生成器及异步迭代器使得处理状态和异步操作变得更为简单。