JavaScriptでは、Symbol.asyncIteratorを使用することで非同期のイテレーション処理を実装できます。この記事では、Symbol.asyncIteratorの基本的な使い方と実践的な例を紹介します。

Symbol.asyncIteratorとは

Symbol.asyncIteratorは、オブジェクトの非同期イテレーションプロトコルを定義するためのSymbolです。これを実装することで、for await...of文を使用して非同期にデータを取得できます。

基本的な実装方法

const asyncIterable = {
  [Symbol.asyncIterator]() {
    return {
      i: 0,
      async next() {
        if (this.i < 3) {
          return { value: this.i++, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

async function example() {
  for await (const value of asyncIterable) {
    console.log(value); // 0, 1, 2
  }
}

example();

実践的な使用例

APIからデータを取得する例

class AsyncDataFetcher {
  constructor(urls) {
    this.urls = urls;
    this.index = 0;
  }

  [Symbol.asyncIterator]() {
    return {
      urls: this.urls,
      index: 0,
      async next() {
        if (this.index < this.urls.length) {
          const response = await fetch(this.urls[this.index]);
          const data = await response.json();
          this.index++;
          return { value: data, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
}

// 使用例
const urls = [
  'https://api.example.com/data1',
  'https://api.example.com/data2',
  'https://api.example.com/data3'
];

const fetcher = new AsyncDataFetcher(urls);

async function fetchData() {
  for await (const data of fetcher) {
    console.log(data);
  }
}

ストリーミングデータの処理

class DataStream {
  constructor() {
    this.data = [];
    this.subscribers = [];
  }

  addData(value) {
    this.data.push(value);
    this.subscribers.forEach(fn => fn());
  }

  [Symbol.asyncIterator]() {
    let index = 0;
    return {
      next: () => {
        return new Promise(resolve => {
          const checkData = () => {
            if (index < this.data.length) {
              resolve({ value: this.data[index++], done: false });
            } else {
              this.subscribers.push(checkData);
            }
          };
          checkData();
        });
      }
    };
  }
}

エラーハンドリング

非同期イテレーターでのエラー処理は、try-catchブロックを使用して行います:

async function handleAsyncIteration() {
  try {
    for await (const value of asyncIterable) {
      console.log(value);
    }
  } catch (error) {
    console.error('エラーが発生しました:', error);
  }
}

パフォーマンスの考慮事項

  • メモリ使用量を考慮し、必要に応じてイテレーターをクリーンアップする
  • 大量のデータを処理する場合は、バッチ処理を検討する
  • 無限ループを避けるため、適切な終了条件を設定する

Symbol.asyncIteratorは、非同期データの取り扱いを効率的に行うための強力な機能です。APIとの通信やストリーミングデータの処理など、様々なユースケースで活用できます。適切に実装することで、より柔軟で保守性の高いコードを作成することができます。