DevToolBoxฟรี
บล็อก

JavaScript Generators และ Iterators: คู่มือสมบูรณ์ 2026

12 นาทีโดย DevToolBox

JavaScript generators (function*) คือฟังก์ชันพิเศษที่สามารถหยุดชั่วคราวและดำเนินการต่อได้โดยใช้ yield

พื้นฐาน Generator: function* และ yield

ฟังก์ชัน generator ส่งคืน generator object ที่ implements Iterator protocol

// Generator function syntax: function* with yield
function* counter(start = 0) {
    let i = start;
    while (true) {
        yield i++;    // pauses here, returns i, resumes on next()
    }
}

const gen = counter(1);
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }

// Finite generator
function* range(start, end, step = 1) {
    for (let i = start; i < end; i += step) {
        yield i;
    }
}

// for...of automatically calls .next() and stops at done: true
for (const num of range(0, 10, 2)) {
    console.log(num); // 0, 2, 4, 6, 8
}

// Spread operator works with generators
const nums = [...range(1, 6)]; // [1, 2, 3, 4, 5]

// Destructuring works too
const [a, b, c] = range(10, 20); // a=10, b=11, c=12

การสื่อสารสองทาง: yield เป็น Expression

yield ไม่ใช่แค่ return มันเป็น expression ที่รับค่าผ่าน next(value)

// Generators support two-way communication
// yield receives values via next(value)
function* calculator() {
    let result = 0;
    while (true) {
        const input = yield result;  // pauses and sends result; receives input
        if (input === null) break;
        result += input;
    }
    return result;
}

const calc = calculator();
calc.next();        // start: { value: 0, done: false }
calc.next(10);      // add 10: { value: 10, done: false }
calc.next(5);       // add 5: { value: 15, done: false }
calc.next(null);    // stop: { value: 15, done: true }

// Generator as stateful iterator
function* idGenerator(prefix = 'id') {
    let id = 1;
    while (true) {
        const reset = yield `${prefix}-${id}`;
        if (reset) {
            id = 1;
        } else {
            id++;
        }
    }
}

const ids = idGenerator('user');
console.log(ids.next().value);       // 'user-1'
console.log(ids.next().value);       // 'user-2'
console.log(ids.next(true).value);   // 'user-1' (reset)
console.log(ids.next().value);       // 'user-2'

yield*: การมอบหมายให้ Iterables อื่น

yield* มอบหมายให้ iterable อื่น

// yield* — delegate to another iterable
function* innerGen() {
    yield 'a';
    yield 'b';
    yield 'c';
}

function* outerGen() {
    yield 1;
    yield* innerGen();    // delegate: yields 'a', 'b', 'c'
    yield* [4, 5, 6];    // works with any iterable
    yield 7;
}

console.log([...outerGen()]); // [1, 'a', 'b', 'c', 4, 5, 6, 7]

// Practical: flatten nested arrays
function* flatten(arr) {
    for (const item of arr) {
        if (Array.isArray(item)) {
            yield* flatten(item); // recursive delegation
        } else {
            yield item;
        }
    }
}

const nested = [1, [2, [3, 4], 5], [6, 7]];
console.log([...flatten(nested)]); // [1, 2, 3, 4, 5, 6, 7]

// Tree traversal with yield*
function* walkTree(node) {
    yield node.value;
    for (const child of node.children ?? []) {
        yield* walkTree(child); // depth-first traversal
    }
}

Async Generators: การ Streaming ข้อมูล

Async generators รวม generators กับ async/await สำหรับการ streaming

// Async generators: async function* with yield
async function* streamLines(url) {
    const response = await fetch(url);
    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let buffer = '';

    while (true) {
        const { done, value } = await reader.read();
        if (done) {
            if (buffer) yield buffer;
            break;
        }
        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split('\n');
        buffer = lines.pop() ?? '';
        for (const line of lines) {
            yield line;   // yield each complete line
        }
    }
}

// Usage with for await...of
async function processCSV(url) {
    let lineNumber = 0;
    for await (const line of streamLines(url)) {
        lineNumber++;
        if (lineNumber === 1) continue; // skip header
        const [name, score] = line.split(',');
        console.log(`${name}: ${score}`);
    }
}

// SSE (Server-Sent Events) as async generator
async function* sseStream(url) {
    const response = await fetch(url);
    const reader = response.body.getReader();
    const decoder = new TextDecoder();

    for await (const chunk of readChunks(reader)) {
        const text = decoder.decode(chunk);
        for (const line of text.split('\n')) {
            if (line.startsWith('data: ')) {
                yield JSON.parse(line.slice(6));
            }
        }
    }
}

รูปแบบในโลกจริง

Generators ช่วยให้สร้าง lazy pipelines ที่ประมวลผลเฉพาะข้อมูลที่จำเป็น

// Real-world: infinite scroll with generator
function* paginator(fetchPage) {
    let page = 1;
    let hasMore = true;

    while (hasMore) {
        const { items, totalPages } = yield fetchPage(page);
        hasMore = page < totalPages;
        page++;
    }
}

// Lazy pipeline with generators
function* map(iterable, fn) {
    for (const item of iterable) {
        yield fn(item);
    }
}

function* filter(iterable, predicate) {
    for (const item of iterable) {
        if (predicate(item)) yield item;
    }
}

function* take(iterable, n) {
    let count = 0;
    for (const item of iterable) {
        if (count++ >= n) break;
        yield item;
    }
}

// Lazy pipeline — no intermediate arrays created!
const first10EvenSquares = [
    ...take(
        filter(
            map(range(1, Infinity), x => x * x),
            x => x % 2 === 0
        ),
        10
    )
];
// [4, 16, 36, 64, 100, 144, 196, 256, 324, 400]

// Observable-like: cancelable async iteration
async function* withTimeout(asyncIterable, timeoutMs) {
    const timeout = setTimeout(() => {
        throw new Error('Stream timed out');
    }, timeoutMs);
    try {
        for await (const item of asyncIterable) {
            yield item;
        }
    } finally {
        clearTimeout(timeout);
    }
}

Generators vs ทางเลือก

FeatureGeneratorasync/awaitPromiseObservable
Infinite sequencesPerfectNoNoYes
Lazy evaluationYes (pull-based)NoNoYes (push)
BackpressureNatural (pull)NoNoYes
Streaming asyncasync function*NoNoYes (RxJS)
Two-way commsyield expressionNoNoNo
Browser supportES2015+ (all)ES2017+ES2015+Requires RxJS

แนวปฏิบัติที่ดีที่สุด

  • ใช้ generators สำหรับ lazy sequences async/await สำหรับ async ครั้งเดียว
  • ใช้ return สำหรับออกก่อน try/finally สำหรับ cleanup
  • Async generators เหมาะกับ HTTP streaming, SSE, WebSocket
  • สร้าง pipeline helpers ที่ใช้ซ้ำได้ (map, filter, take)
  • TypeScript: annotate ด้วย Generator type

คำถามที่พบบ่อย

Generator กับ Iterator ต่างกันอย่างไร?

Iterator คือ object ที่มี next() Generator คือฟังก์ชันที่สร้างและจัดการ iterator อัตโนมัติ

Generators สามารถแทนที่ async/await ได้ไหม?

สำหรับ operation ครั้งเดียว ไม่ได้ สำหรับ streaming async generators ดีกว่า

Iterator Protocol คืออะไร?

Object ที่มี next() คืน { value, done } และ [Symbol.iterator]() สำหรับ iterable

Generators เหมาะกับ Redux-Saga ไหม?

ใช่ Redux-Saga ใช้สำหรับ effects ที่ test ได้

จะยกเลิก generator ได้อย่างไร?

เรียก generator.return(value) หรือใช้ break ใน for...of

เครื่องมือที่เกี่ยวข้อง

𝕏 Twitterin LinkedIn
บทความนี้มีประโยชน์ไหม?

อัปเดตข่าวสาร

รับเคล็ดลับการพัฒนาและเครื่องมือใหม่ทุกสัปดาห์

ไม่มีสแปม ยกเลิกได้ตลอดเวลา

ลองเครื่องมือที่เกี่ยวข้อง

{ }JSON FormatterB→Base64 Encode Online

บทความที่เกี่ยวข้อง

JavaScript Closures อธิบาย: Scope, Memory และ Patterns จริง

เจาะลึก JavaScript closures 2026: lexical scope, ผลกระทบต่อ memory, memoization, module pattern และ currying

TypeScript Type Guards: คู่มือการตรวจสอบประเภทขณะรันไทม์

เชี่ยวชาญ TypeScript type guards: typeof, instanceof, in และ guard แบบกำหนดเอง

React Query Patterns 2026: Data Fetching, Caching และ Mutations ด้วย TanStack Query

เชี่ยวชาญ React Query (TanStack Query) patterns 2026: useQuery, useMutation, optimistic updates