阿里云主机折上折
  • 微信号
Current Site:Index > The relationship between generators and iterators

The relationship between generators and iterators

Author:Chuan Chen 阅读数:55096人阅读 分类: JavaScript

Basic Concepts of Generators and Iterators

ECMAScript 6 introduced two closely related concepts: generators and iterators. Generators are special functions that can pause and resume execution, while iterators are interfaces for traversing data structures. Together, they bring more powerful asynchronous programming capabilities and more flexible data traversal methods to JavaScript.

An iterator is an object that implements the iterator protocol, meaning it has a next() method. Each call to next() returns an object with value and done properties:

const simpleIterator = {
  [Symbol.iterator]() {
    let count = 0;
    return {
      next() {
        return count < 3 
          ? { value: count++, done: false }
          : { value: undefined, done: true };
      }
    };
  }
};

for (const item of simpleIterator) {
  console.log(item); // Output: 0, 1, 2
}

Definition and Execution of Generator Functions

Generator functions are defined using the function* syntax and can use the yield keyword internally to pause execution and return a value. Calling a generator function does not immediately execute its body but instead returns a generator object:

function* simpleGenerator() {
  yield 1;
  yield 2;
  return 3;
}

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

Generator objects are both iterators and iterables, meaning they can be used in for...of loops:

function* countToThree() {
  yield 1;
  yield 2;
  yield 3;
}

for (const num of countToThree()) {
  console.log(num); // Outputs sequentially: 1, 2, 3
}

Generators as Iterator Factories

An important feature of generators is their ability to act as factory functions for iterators. Each call to a generator function creates a new iterator instance, making generators particularly suitable for creating iterable objects:

const iterableObject = {
  *[Symbol.iterator]() {
    yield 'a';
    yield 'b';
    yield 'c';
  }
};

for (const letter of iterableObject) {
  console.log(letter); // Output: a, b, c
}

The yield* Expression and Iterator Delegation

The yield* expression allows a generator to delegate iteration control to another iterable object or generator, which is especially useful when combining multiple iterators:

function* generatorA() {
  yield 'a';
  yield 'b';
}

function* generatorB() {
  yield 'x';
  yield* generatorA();
  yield 'y';
}

for (const val of generatorB()) {
  console.log(val); // Output: x, a, b, y
}

Bidirectional Communication with Generators and Iterators

Generators can not only produce values via yield but also receive values passed externally through the next() method, enabling bidirectional communication:

function* twoWayGenerator() {
  const name = yield 'What is your name?';
  const age = yield `Hello ${name}, how old are you?`;
  return `${name} is ${age} years old`;
}

const gen = twoWayGenerator();
console.log(gen.next().value); // "What is your name?"
console.log(gen.next('Alice').value); // "Hello Alice, how old are you?"
console.log(gen.next(30).value); // "Alice is 30 years old"

Asynchronous Iterators and Asynchronous Generators

ES2018 introduced asynchronous iterators and asynchronous generators for handling asynchronous data streams. Asynchronous generators are defined using the async function* syntax and can yield Promises:

async function* asyncNumbers() {
  let i = 0;
  while (i < 3) {
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i++;
  }
}

(async () => {
  for await (const num of asyncNumbers()) {
    console.log(num); // Outputs sequentially with 100ms intervals: 0, 1, 2
  }
})();

Practical Applications of Generators and Iterators

Generators and iterators have various practical applications in development, including but not limited to:

  1. Lazy Evaluation: Computing values only when needed, saving memory
  2. Infinite Sequences: Representing theoretically infinite data streams
  3. State Machines: Implementing complex state transition logic with generators
  4. Asynchronous Flow Control: Achieving effects similar to async/await in combination with Promises
// Infinite sequence example
function* fibonacci() {
  let [prev, curr] = [0, 1];
  while (true) {
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

const fib = fibonacci();
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2
console.log(fib.next().value); // 3
// Can continue indefinitely...

Performance Considerations for Generators and Iterators

While generators and iterators provide powerful abstraction capabilities, performance-sensitive scenarios require attention:

  1. Generator calls have additional overhead compared to regular function calls
  2. The iterator protocol has additional overhead compared to direct loops
  3. Trade-offs between abstraction and performance may be needed in hot code paths
// Performance comparison example
function regularSum(arr) {
  let sum = 0;
  for (let i = 0; i < arr.length; i++) {
    sum += arr[i];
  }
  return sum;
}

function* arrayIterator(arr) {
  for (const item of arr) {
    yield item;
  }
}

function iteratorSum(arr) {
  let sum = 0;
  for (const item of arrayIterator(arr)) {
    sum += item;
  }
  return sum;
}

// For large arrays, regularSum is typically faster than iteratorSum

Combining Generators and Iterators

Generators and iterators can be combined to create more complex data processing pipelines. This pattern is particularly common in functional programming:

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

function* map(iterable, mapper) {
  for (const item of iterable) {
    yield mapper(item);
  }
}

const numbers = [1, 2, 3, 4, 5];
const result = map(
  filter(numbers, n => n % 2 === 0),
  n => n * 2
);

console.log([...result]); // [4, 8]

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.