「イテレーター/ジェネレーターを使って現実の非同期処理をスマートに扱う」ユースケースを、初心者にも分かるように具体例で紹介します。
ここでは代表的な 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);
}
})();
JavaScriptfor await...of にするだけで、非同期データを順に取り出すループが実現できます。
これがまさに async iterator(非同期イテレーター)の力です。
まとめ:ユースケース別に整理
| ユースケース | 適した種類 | 利点 |
|---|---|---|
| 巨大ファイルを分割して処理 | function* / async function* | メモリ節約・中断再開可 |
| 無限列・逐次生成 | function* | 遅延生成・パフォーマンス向上 |
| 非同期APIを順に処理 | async function* | 順序保証・シンプル制御 |
| ストリーミングAPI(チャット・センサーなど) | async function* | 逐次的なリアルタイム処理 |

