2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

非同期イテレーションの本質:for await...ofで書く次世代JavaScriptの反復処理

Posted at

概要

非同期処理の本質は「完了を待たずに制御を進める」こと。
反復処理の本質は「ひとつずつ処理する」こと。

この2つを統合するのが、for await...of という構文だ。

Promiseでは「まとめて非同期」、
for await...of では「ひとつずつ非同期」。
その違いを理解すると、非同期処理の設計力は一段階上のステージへ進化する。


対象環境

ES2018 以降(Node.js 10+ / モダンブラウザ)

for await...of の基本構文

for await (const item of asyncIterable) {
  // 非同期に1つずつ処理
}
  • asyncIterable は非同期イテラブル(AsyncIterator)である必要がある
  • Symbol.asyncIterator を持つオブジェクト
  • 1つずつ await しながら処理を進める

対応するオブジェクト

オブジェクト例 対応
通常の配列
配列 of Promise
AsyncGenerator
ReadableStream(Web / Node.js)
自作で Symbol.asyncIterator 実装

実例①:非同期ジェネレーターとの併用

async function* fetchData() {
  yield await Promise.resolve('step1');
  yield await Promise.resolve('step2');
  yield await Promise.resolve('step3');
}

for await (const step of fetchData()) {
  console.log(step); // 'step1' → 'step2' → 'step3'
}

→ ✅ 各ステップを順番に“待ってから処理”


実例②:Node.jsの fs.createReadStream

import * as fs from 'fs';

const stream = fs.createReadStream('./log.txt', { encoding: 'utf8' });

for await (const chunk of stream) {
  console.log('chunk:', chunk);
}

→ ✅ ストリームの読み込みを逐次的に await 処理できる


実例③:Web APIとの逐次通信(Paginated API)

async function* fetchPages() {
  let page = 1;
  while (true) {
    const res = await fetch(`/api/data?page=${page}`);
    const data = await res.json();
    if (data.length === 0) break;
    yield* data;
    page++;
  }
}

for await (const item of fetchPages()) {
  console.log('item:', item);
}

→ ✅ 「まとめてfetchしない」設計が可能に
→ ✅ ページ単位での中断やリトライにも強い


カスタム非同期イテレータの作り方

const customAsyncIterable = {
  async *[Symbol.asyncIterator]() {
    yield 'one';
    await new Promise(res => setTimeout(res, 100));
    yield 'two';
  }
};

for await (const value of customAsyncIterable) {
  console.log(value);
}

よくある誤解

❌ Promise配列には使えない

const promises = [Promise.resolve(1), Promise.resolve(2)];
for await (const p of promises) {
  // ✅ 実は動くが “AsyncIterator” ではない
}

→ これは特殊ケースの挙動
→ 推奨される使い方ではない(読みやすさ・明示性に欠ける)


設計上の使いどころ

状況 for await...of を使うべきか? 理由
ストリーミング受信 ✅ Yes チャンクごとに処理できる
非同期ジェネレーターで処理を分割したい ✅ Yes 柔軟な制御構造を構築できる
複数Promiseを一括で処理したい ❌ No Promise.all() のほうが適切
APIページネーション / キュー処理 ✅ Yes 非同期逐次処理に強い

結語

非同期処理は「まとめて実行する」ものではない。
「順番に受け取り、順番に処理する」ものとして設計することができる。

  • 一括で処理 → Promise.all()
  • 分割して処理 → for await...of

この設計選択こそが、非同期処理を“支配する”ための鍵である。

for await...of は、非同期処理に構造を与える構文であり、
リアクティブでスケーラブルな処理設計を可能にする武器である。

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?