JavaScript | 第16章「イテレーターとジェネレーター」

javascrpit JavaScript
スポンサーリンク

「イテレーター/ジェネレーターを使って現実の非同期処理をスマートに扱う」ユースケースを、初心者にも分かるように具体例で紹介します。

ここでは代表的な 2 例を取り上げます。


ユースケース①:大きな CSV ファイルを「少しずつ」処理する(メモリ節約)

背景

普通のやり方だと:

const data = await fetch("bigdata.csv").then(res => res.text());
const lines = data.split("\n");
JavaScript

ファイル全体を一度に読み込むので、数百 MB 以上あるとメモリが爆発します💥。

ジェネレーターで「1行ずつ読み出す」

ブラウザでは ReadableStream(または Node.js の fs.createReadStream)と組み合わせて、
ジェネレーターで1行ずつ処理する方法が取れます。


例:CSVをストリーム的に処理するジェネレーター

// CSV文字列を1行ずつ返すジェネレーター
function* csvLineGenerator(csvText) {
  const lines = csvText.split('\n');
  for (const line of lines) {
    yield line.split(','); // 1行を配列にして返す
  }
}

// 疑似的に大きなCSVデータを用意
const csvData = "id,name,score\n1,Tom,85\n2,Alice,90\n3,Bob,78";

// ジェネレーターで順に処理
const reader = csvLineGenerator(csvData);
for (const row of reader) {
  console.log(row);
}
JavaScript

出力:

["id", "name", "score"]
["1", "Tom", "85"]
["2", "Alice", "90"]
["3", "Bob", "78"]

これを Node.js で fs.createReadStream と組み合わせると、
「巨大CSVを数行ずつ処理 → DBに登録 → 次の行へ」という省メモリ処理が可能になります。

応用:非同期ストリーム処理(async generator)

大きなファイルやネットワークレスポンスは非同期で少しずつ届くので、
async function* を使うと理想的です。

async function* streamCSV(url) {
  const response = await fetch(url);
  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  let chunk = '';
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    chunk += decoder.decode(value, { stream: true });
    let lines = chunk.split('\n');
    chunk = lines.pop(); // 最後の行が途中なら保持

    for (const line of lines) {
      yield line.split(',');
    }
  }
  if (chunk) yield chunk.split(',');
}

// 利用例(擬似URL)
(async () => {
  for await (const row of streamCSV('https://example.com/big.csv')) {
    console.log('Row:', row);
  }
})();
JavaScript

これで巨大なCSVでも「1行ずつ」リアルタイムで処理できます。
👉 メモリ効率◎、途中停止・再開◎、リアルタイム処理◎

ユースケース②:複数の API を順に呼び出す(非同期APIの逐次制御)

背景

Promiseチェーンや forEach(async …) だと「順番通りに動かない」ことがあります。

// NG: 並列で動いて順序バラバラ
['A','B','C'].forEach(async x => {
  await fetchData(x);
});
JavaScript

ジェネレーターで「逐次実行」を制御する

ジェネレーター+Promise+for...of を組み合わせると順番制御が簡単になります。


例:APIを順に呼び出して結果を逐次表示

function* apiTaskGenerator() {
  yield fetch('https://jsonplaceholder.typicode.com/todos/1');
  yield fetch('https://jsonplaceholder.typicode.com/todos/2');
  yield fetch('https://jsonplaceholder.typicode.com/todos/3');
}

// 非同期で順に実行
async function runGenerator(gen) {
  for (const task of gen) {
    const res = await task;
    const data = await res.json();
    console.log(data.title);
  }
}

runGenerator(apiTaskGenerator());
JavaScript

出力(順に実行される):

delectus aut autem
quis ut nam facilis et officia qui
fugiat veniam minus

👉 順序保証あり+シンプルな流れ制御

もう少し実践的:async generator + for await...of

async function* fetchSequentially(ids) {
  for (const id of ids) {
    const res = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`);
    const data = await res.json();
    yield data; // 完了したら結果を1件ずつ返す
  }
}

(async () => {
  for await (const todo of fetchSequentially([1, 2, 3])) {
    console.log(todo.title);
  }
})();
JavaScript

for await...of にするだけで、非同期データを順に取り出すループが実現できます。
これがまさに async iterator(非同期イテレーター)の力です。

まとめ:ユースケース別に整理

ユースケース適した種類利点
巨大ファイルを分割して処理function* / async function*メモリ節約・中断再開可
無限列・逐次生成function*遅延生成・パフォーマンス向上
非同期APIを順に処理async function*順序保証・シンプル制御
ストリーミングAPI(チャット・センサーなど)async function*逐次的なリアルタイム処理
タイトルとURLをコピーしました