迭代器與產生器
迭代器(Iterator)
// 可迭代物件都有 Symbol.iterator 方法
let arr = [10, 20, 30];
let iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 10, done: false }
console.log(iterator.next()); // { value: 20, done: false }
console.log(iterator.next()); // { value: 30, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
// for...of 背後就是在用迭代器
for (let item of arr) {
console.log(item); // 10, 20, 30
}
自訂可迭代物件
let range = {
from: 1,
to: 5,
// 實作 Symbol.iterator
[Symbol.iterator]() {
let current = this.from;
let last = this.to;
return {
next() {
return current <= last
? { value: current++, done: false }
: { done: true };
}
};
}
};
for (let num of range) {
console.log(num); // 1, 2, 3, 4, 5
}
// 也可以用展開運算子
let nums = [...range]; // [1, 2, 3, 4, 5]
產生器(Generator)
// function* 定義產生器函式
function* numberGenerator() {
yield 1; // ← 暫停,回傳 1
yield 2; // ← 暫停,回傳 2
yield 3; // ← 暫停,回傳 3
}
let gen = numberGenerator();
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()); // { value: undefined, done: true }
// 可以用 for...of
for (let n of numberGenerator()) {
console.log(n); // 1, 2, 3
}
產生器的實用場景
無限序列
function* fibonacci() {
let a = 0, b = 1;
while (true) { // ← 無限迴圈也沒問題!
yield a; // ← 每次 next() 才執行到這裡
[a, b] = [b, a + b];
}
}
let 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
// 取前 10 個
function take(gen, n) {
let result = [];
for (let i = 0; i < n; i++) {
result.push(gen.next().value);
}
return result;
}
console.log(take(fibonacci(), 10));
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
ID 產生器
function* idGenerator(prefix = "id") {
let id = 1;
while (true) {
yield `${prefix}_${id++}`;
}
}
let userIds = idGenerator("user");
console.log(userIds.next().value); // "user_1"
console.log(userIds.next().value); // "user_2"
async 產生器
// 非同步產生器
async function* fetchPages(url) {
let page = 1;
while (true) {
let response = await fetch(`${url}?page=${page}`);
let data = await response.json();
if (data.length === 0) return; // ← 沒資料就停
yield data;
page++;
}
}
// 使用 for await...of
async function getAllUsers() {
let allUsers = [];
for await (let page of fetchPages("/api/users")) {
allUsers.push(...page);
}
return allUsers;
}