JavaScript/ReadableStreamDefaultReader

ReadableStreamDefaultReaderオブジェクト

概要

ReadableStreamDefaultReaderは、ストリームからデータを逐次的に読み取るためのインターフェースを提供します。このオブジェクトを使用することで、ReadableStreamからチャンクごとにデータを取得し、非同期処理することが可能になります。

基本的な使い方

ReadableStreamDefaultReaderは、ReadableStreamのgetReader()メソッドを呼び出すことで取得できます。

const response = await fetch('https://example.com/data');
const reader = response.body.getReader();

プロパティとメソッド

ReadableStreamDefaultReaderには以下のプロパティとメソッドが含まれています。

closed

closedプロパティは、ストリームが閉じられたときに解決するPromiseを返します。

const reader = stream.getReader();
reader.closed.then(() => {
  console.log('ストリームが閉じられました');
});

cancel(reason)

cancelメソッドは、ストリームの読み取りをキャンセルします。

const reader = stream.getReader();
// 読み取りをキャンセル
reader.cancel('読み取りを中止します').then(() => {
  console.log('ストリームの読み取りがキャンセルされました');
});

read()

readメソッドは、ストリームから次のチャンクを読み取ります。このメソッドは、{ value, done }オブジェクトを含むPromiseを返します。doneがtrueの場合、ストリームの終わりに達したことを意味します。

const reader = stream.getReader();

async function readStream() {
  while (true) {
    const { value, done } = await reader.read();
    
    if (done) {
      console.log('ストリームの読み取りが完了しました');
      break;
    }
    
    console.log(`受信したデータ: ${value}`);
  }
}

readStream();

releaseLock()

releaseLockメソッドは、リーダーのロックを解除し、他のリーダーがストリームを読み取れるようにします。

const reader = stream.getReader();
// 何らかの処理を行った後...
reader.releaseLock();
// 別のリーダーで読み取りが可能になる
const anotherReader = stream.getReader();

実践的な例:テキストファイルの読み取り

下記は、fetchを使ってテキストファイルを取得し、ReadableStreamDefaultReaderを使って処理する例です。

async function readTextFile(url) {
  const response = await fetch(url);
  const reader = response.body.getReader();
  const decoder = new TextDecoder('utf-8');
  let result = '';
  
  while (true) {
    const { value, done } = await reader.read();
    
    if (done) break;
    
    // Uint8Arrayをテキストに変換
    const chunk = decoder.decode(value, { stream: true });
    result += chunk;
    
    // 処理中の経過を表示
    console.log(`${result.length}バイト読み込みました`);
  }
  
  return result;
}

readTextFile('https://example.com/sample.txt')
  .then(text => {
    console.log('ファイルの内容:', text);
  })
  .catch(error => {
    console.error('エラーが発生しました:', error);
  });

ReadableStreamDefaultReaderとジェネレーターの組み合わせ

ストリームを使いやすく処理するために、ジェネレーター関数と組み合わせることができます。

async function* streamAsyncIterator(stream) {
  const reader = stream.getReader();
  try {
    while (true) {
      const { value, done } = await reader.read();
      if (done) return;
      yield value;
    }
  } finally {
    reader.releaseLock();
  }
}

async function processStream() {
  const response = await fetch('https://example.com/data');
  const decoder = new TextDecoder();
  
  for await (const chunk of streamAsyncIterator(response.body)) {
    const text = decoder.decode(chunk);
    console.log('チャンク:', text);
  }
}

processStream();

エラー処理

ReadableStreamDefaultReaderを使用する際は、適切なエラー処理を行うことが重要です。

async function safelyReadStream(stream) {
  const reader = stream.getReader();
  
  try {
    while (true) {
      const { value, done } = await reader.read();
      if (done) break;
      
      // データの処理
      console.log('データチャンク:', value);
    }
  } catch (error) {
    console.error('ストリーム読み取り中にエラーが発生しました:', error);
  } finally {
    // リーダーのロックを必ず解除
    reader.releaseLock();
  }
}

ReadableStreamDefaultReaderの状態と戻り値

メソッド 戻り値 説明
read() Promise<{value: any, done: boolean}> 次のチャンクを読み取り、値とストリームの状態を返す
cancel(reason) Promise<void> ストリームをキャンセルし、完了すると解決するPromiseを返す
releaseLock() void リーダーのロックを解除する(戻り値なし)
closed Promise<void> ストリームが閉じられたとき、またはエラーが発生したときに解決または拒否されるPromiseを返す

互換性とブラウザサポート

ReadableStreamDefaultReaderは比較的新しいAPIであり、一部の古いブラウザではサポートされていない場合があります。必要に応じてポリフィルを使用することも検討してください。

// ブラウザの互換性をチェックする簡単な方法
if (typeof ReadableStream === 'undefined') {
  console.warn('このブラウザはReadableStreamをサポートしていません。ポリフィルが必要かもしれません。');
}

このようにReadableStreamDefaultReaderは、大きなファイルや長時間にわたるデータ転送を効率的に処理するための強力なツールです。ストリーミングAPIを活用することで、メモリ使用量を最小限に抑えながら大量のデータを処理することができます。

カテゴリ:JavaScript
カテゴリ:JavaScript