ジェネレーター関数を用いたJavaScriptでの効率的なデータストリーミング

ジェネレーター関数を用いたJavaScriptでの効率的なデータストリーミング

ジェネレーター関数は、JavaScriptで遅延評価やデータストリーミングを実現するための強力な機能です。この記事では、ジェネレーター関数の基本から応用までを詳しく取り上げ、効率的なデータ処理の方法を説明します。

ジェネレーター関数の基本

function* simpleGenerator() {
  yield 'データ1';
  yield 'データ2';
  yield 'データ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: false }
console.log(gen.next()); // { value: undefined, done: true }

無限ストリームの生成

function* infiniteCounter() {
  let count = 0;
  while (true) {
    yield count++;
  }
}

const counter = infiniteCounter();
console.log(counter.next().value); // 0
console.log(counter.next().value); // 1
console.log(counter.next().value); // 2

配列をジェネレーターで処理

function* arrayProcessor(array) {
  for (const item of array) {
    yield item;
  }
}

const arrayGen = arrayProcessor([10, 20, 30]);
console.log(arrayGen.next().value); // 10
console.log(arrayGen.next().value); // 20
console.log(arrayGen.next().value); // 30

非同期ジェネレーター

async function* fetchData(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    const data = await response.json();
    yield data;
  }
}

const urls = ['https://api.example.com/1', 'https://api.example.com/2'];

(async () => {
  for await (const data of fetchData(urls)) {
    console.log(data);
  }
})();

ジェネレーターとイテレーターの連携

function* nestedGenerators() {
  yield* [1, 2, 3];
  yield* [4, 5, 6];
}

const nestedGen = nestedGenerators();
console.log([...nestedGen]); // [1, 2, 3, 4, 5, 6]

ジェネレーターでデータ処理をパイプライン化

function* filterGenerator(numbers, predicate) {
  for (const num of numbers) {
    if (predicate(num)) {
      yield num;
    }
  }
}

function* mapGenerator(numbers, mapper) {
  for (const num of numbers) {
    yield mapper(num);
  }
}

const numbers = [1, 2, 3, 4, 5];
const filtered = filterGenerator(numbers, n => n % 2 === 0);
const mapped = mapGenerator(filtered, n => n * 10);

console.log([...mapped]); // [20, 40]

ジェネレーターを使ったカスタムデータストリーム

function* customDataStream(start, step, limit) {
  let current = start;
  while (current <= limit) {
    yield current;
    current += step;
  }
}

const stream = customDataStream(0, 5, 20);
console.log([...stream]); // [0, 5, 10, 15, 20]

非同期ジェネレーターを用いたリアルタイム処理

async function* eventStream(eventSource) {
  const queue = [];
  eventSource.addEventListener('data', event => queue.push(event));

  while (true) {
    if (queue.length > 0) {
      yield queue.shift();
    } else {
      await new Promise(resolve => setTimeout(resolve, 100));
    }
  }
}

ストリームAPIとジェネレーターの組み合わせ

async function* textStream(reader) {
  const decoder = new TextDecoder();
  let result;
  while (!(result = await reader.read()).done) {
    yield decoder.decode(result.value, { stream: true });
  }
}

エラーハンドリングの追加

function* errorHandlingGenerator() {
  try {
    yield '正常なデータ';
    throw new Error('エラー発生');
  } catch (error) {
    yield `エラー: ${error.message}`;
  }
}

const genWithError = errorHandlingGenerator();
console.log(genWithError.next().value); // '正常なデータ'
console.log(genWithError.next().value); // 'エラー: エラー発生'

ジェネレーターの状態管理

function* statefulGenerator() {
  let state = 0;
  while (true) {
    state += yield state;
  }
}

const statefulGen = statefulGenerator();
console.log(statefulGen.next().value); // 0
console.log(statefulGen.next(5).value); // 5
console.log(statefulGen.next(10).value); // 15

まとめ

ジェネレーター関数は、効率的なデータストリーミングと柔軟なデータ処理を可能にします。シンプルな配列処理から非同期のリアルタイムデータ処理まで、多様なシナリオで活用できます。