JavaScript Promise, async, await


Promise

Promiseは、JavaScriptにおける非同期処理の1つのパターンであり、非同期操作の結果を表現するオブジェクトです。Promiseは3つの状態を持ちます:

  1. pending(保留中) - 最初の状態、最終的にfulfilledかrejectedになります。

  2. fulfilled(達成) - 操作が成功した場合の状態。

  3. rejected(拒否) - 操作が失敗した場合の状態。

Promiseは以下のように作成できます:

let promise = new Promise(function(resolve, reject) {
    // 非同期操作

    if (/* 操作が成功 */) {
        resolve('成功の結果');
    } else {
        reject('エラー');
    }
});

Promiseがfulfilledまたはrejectedになると、それぞれ.thenまたは.catchメソッドを使って結果を取得することができます:

promise
    .then(function(result) {
        console.log(result);  // '成功の結果' が表示される
    })
    .catch(function(error) {
        console.error(error);  // 'エラー' が表示される
    });

また、.finallyメソッドを使って、成功か失敗かに関わらず実行される処理を記述することもできます。

Promiseチェーンもサポートしており、複数の非同期処理を順番に実行することができます:

function doFirstTask() {
    return new Promise((resolve, reject) => {
        // 処理...
        resolve('First task result');
    });
}

function doSecondTask(dataFromFirstTask) {
    return new Promise((resolve, reject) => {
        // 処理...
        resolve('Second task result');
    });
}

doFirstTask()
    .then(doSecondTask)
    .then(result => {
        console.log(result);  // 'Second task result' が表示される
    })
    .catch(error => {
        console.error(error);
    });

また、Promise.allPromise.raceといったメソッドもあり、複数のPromiseを同時に制御することも可能です。

以上が基本的なPromiseの説明です。Promiseを使って非同期処理を行うことで、コードの可読性が向上し、エラーハンドリングも容易になります。


async, await

asyncawaitは、JavaScriptの非同期処理をより読みやすく、直感的に書くための構文です。これを使うと、非同期コードをほとんど同期的に書くことができ、Promiseのチェーンやコールバックのネストを大幅に減少させることができます。

async

asyncキーワードは、関数の前に置かれます。このキーワードを使用して定義された関数は、必ずPromiseを返します。

async function fetchUserData() {
    return 'UserData';
}

fetchUserData().then(data => console.log(data));  // 'UserData' が出力される

もしasync関数内で明示的にPromiseを返さない場合、関数の戻り値は自動的にPromiseでラップされます。

await

awaitキーワードは、async関数の中でPromiseの解決を待つために使用されます。awaitは、Promiseが完了するのを待ち、その結果を返します。

async function fetchData() {
    let response = await fetch('https://api.example.com/data');
    let data = await response.json();
    return data;
}

fetchData().then(data => console.log(data));

上記のコードでは、fetch関数が返すPromiseが解決されるまで次の行(response.json())に進むことはありません。このように、awaitを使用することで非同期処理を順番に実行することができ、コードが非常に読みやすくなります。

注意点

  • awaitキーワードはasync関数の中でしか使えません。

  • 過度にawaitを使用すると、非同期処理が連鎖的になり、パフォーマンスの問題が生じることがあるので注意が必要です。

  • async/awaitを使用しても、エラーハンドリングは必要です。通常、try/catchブロックを使用して、awaitの呼び出しを囲むことでエラーを捕捉します。

async function fetchDataWithHandling() {
    try {
        let response = await fetch('https://api.example.com/data');
        let data = await response.json();
        return data;
    } catch (error) {
        console.error('Error fetching data:', error);
        throw error;  // または適切なエラー処理
    }
}




onload

onloadはデフォルトで非同期。

onload イベントハンドラは、通常のブラウザのイベントハンドラと同じで、非同期的に実行されます。特に FileReader オブジェクトの onload イベントは、readAsDataURL や他の読み込みメソッドを使用してファイルを読み込んだ後に非同期的に発火します。

            const reader = new FileReader();
            reader.onload = (event) => {
                if (mainImage) {  
                    mainImage.src = event.target.result;
                } else {
                    setupMainImage(event);
                }
            }
            reader.readAsDataURL(blob);

上記のコードの説明:

  • FileReader インスタンスの onload イベントハンドラを設定しています。

  • reader.readAsDataURL(blob); でファイルの読み込みを開始します。

  • ファイルの読み込みが完了すると、設定された onload イベントハンドラが非同期的に呼び出されます。

  • この時点で、event.target.result には読み込んだデータのURLが含まれます。

  • そして、mainImage が存在する場合、その src に読み込んだデータのURLを設定します。存在しない場合、setupMainImage(event) 関数を呼び出します。

このように、onload は非同期的な処理の完了をハンドルするためのものです。従って、readAsDataURL が呼び出された直後には、onload が即座に実行されることはありません。ファイルの読み込みが完了した時点で、それが実行されます。

onloadハンドラをasyncにする場合

          const reader = new FileReader();
          reader.onload = async (event) => {
            if (mainImage) {  
              mainImage.src = event.target.result;
            } else {
              setupMainImage(event);
            }
          }
          reader.readAsDataURL(blob);

上記の状態では意味がない。
内部でawaitを用いる場合有効。

reader.onload = async (event) => {
  if (mainImage) {
    mainImage.src = event.target.result;
  } else {
    // 例: setupMainImage が非同期関数であるとする
    await setupMainImageAsync(event);
    console.log("Image setup is complete!");
  }
}

onloadの完了を確実に待ちたい時

Promiseでラップする。

function readAsDataURL(blob) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();

    reader.onload = (event) => {
      resolve(event.target.result);
    };

    reader.onerror = (error) => {
      reject(error);
    };

    reader.readAsDataURL(blob);
  });
}

// 使用例
async function processBlob(blob) {
  try {
    const dataUrl = await readAsDataURL(blob);
    
    if (mainImage) {
      mainImage.src = dataUrl;
    } else {
      setupMainImage(dataUrl);
    }
  } catch (error) {
    console.error("Error reading the blob:", error);
  }
}

// Blob を処理する
processBlob(myBlob);

aync, awaitで書くと

async function readAsDataURL(blob) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();

        reader.onload = (event) => {
            resolve(event.target.result);
        };

        reader.onerror = (error) => {
            reject(error);
        };

        reader.readAsDataURL(blob);
    });
}

async function processBlob(blob) {
    try {
        const dataUrl = await readAsDataURL(blob);
        
        if (mainImage) {
            mainImage.src = dataUrl;
        } else {
            setupMainImage(dataUrl);
        }
    } catch (error) {
        console.error("Error reading the blob:", error);
    }
}

// Blob を処理する
processBlob(myBlob);

これは非同期処理が明示されるだけであり、
Promise自体は消せない。

応用例

onloadハンドラのasyncは不要。
onloadハンドラの内部でawaitを使用するなら必要。
pasteハンドラのasyncも不要。
pasteハンドラの内部でawaitを使用するなら必要。

    pasteArea.addEventListener('paste', async (event) => {
      event.preventDefault();

      const clipboardData = event.clipboardData || window.clipboardData;
      if (!clipboardData) { return; }
  
      const items = clipboardData.items;  
      for (let i = 0; i < items.length; i++) {
        const item = items[i];
        if (item.type.startsWith('image/')) {
          const blob = item.getAsFile();
          const reader = new FileReader();
          reader.onload = async (event) => {
            if (mainImage) {  
              mainImage.src = event.target.result; 
            } else {
              setupMainImage(event);
            }
          }
          reader.readAsDataURL(blob);
        }
      }//for
    });

Promiseラップ形


    function handlePasteEvent(event) {
        event.preventDefault();

        return new Promise((resolve, reject) => {
            const clipboardData = event.clipboardData || window.clipboardData;
            if (!clipboardData) { return; }
    
            const items = clipboardData.items;
    
            for (let i = 0; i < items.length; i++) {
                const item = items[i];
                if (item.type.startsWith('image/')) {
                    const blob = item.getAsFile();
                    const reader = new FileReader();
                    reader.onload = async (event) => {
                        if (mainImage) {
                            mainImage.src = event.target.result;
                        } else {
                            setupMainImage(event);
                        }
                        resolve(mainImage.src);
                    }
                    reader.readAsDataURL(blob);
                }
            }//for
        });
    }

async, await形

    async function handlePasteEvent(event) {
        event.preventDefault();
    
        const clipboardData = event.clipboardData || window.clipboardData;
        if (!clipboardData) { return; }
    
        const items = clipboardData.items;
    
        for (let i = 0; i < items.length; i++) {
            const item = items[i];
            if (item.type.startsWith('image/')) {
                const blob = item.getAsFile();
    
                try {
                    const imageSrc = await readAsDataURL(blob);
                    if (mainImage) {  // 既にmainImageが存在する場合
                        mainImage.src = imageSrc;  // そのsrcを更新
                    } else {
                        setupMainImage({ target: { result: imageSrc } });
                    }
                    return mainImage.src;
                } catch (error) {
                    console.error("Error reading the file:", error);
                }
            }
        }
    }

    function readAsDataURL(file) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = (event) => {
                resolve(event.target.result);
            };
            reader.onerror = (error) => {
                reject(error);
            };
            reader.readAsDataURL(file);
        });
    }


Promiseコンストラクタ内のthisとself

流れで適当にthisとself使ってると痛い目にあう話。

this:

Promiseコンストラクタの中でのthisは、新しく作成されるPromiseオブジェクトを指します。ただし、Promiseコンストラクタの実行コンテキストの中でthisに直接アクセスするのは通常、推奨されません。理由は、Promiseの状態や値を直接操作することができないからです。

const p = new Promise(function(resolve, reject) {
  console.log(this === p); // true
});

self:

グローバルスコープでのselfは、ウェブワーカーかメインスレッドのどちらかでの実行を意識せずに、グローバルオブジェクトを参照するための慣習的な方法です。

  • ブラウザのメインスレッドのコンテキストでは、selfwindowオブジェクトを指します。

  • ウェブワーカーのコンテキストでは、selfはそのワーカーのグローバルスコープを指します。

Promiseコンストラクタの中でselfを見ることは少ないです。もし使われている場合、それは外部のスコープや特定の実行コンテキスト(例: ウェブワーカー)に関連するものである可能性が高いです。
結論として、Promiseコンストラクタの中でのthisは新しいPromiseオブジェクトを指し、selfは実行コンテキストに依存して、通常はグローバルオブジェクトを指します。ただし、どちらの用語もPromiseの動作や解決/拒否のメカニズムに直接関係するわけではありません。


Fetch APIとXMLHttpRequest

最大の違いはXMLHttpRequestがpromiseを想定してないこと

Fetch APIは、ブラウザが提供するモダンなJavaScript APIで、サーバーに対してHTTPリクエストを発行してリソースをネットワークから非同期に取得するための手段です。HTML、JSON、画像、テキストファイルなど、さまざまな種類のデータを取得して扱うことができます。

XMLHttpRequest(XHR)のよりパワフルで柔軟な代替手段として設計されており、プロミス(Promise)ベースのAPIを利用しています。これにより、より簡潔で読みやすいコードを書くことができ、非同期処理の扱いも容易になります。

基本的な使用方法

Fetch APIの基本的な使用方法は非常にシンプルです。最も基本的な形式では、`fetch()`関数にリソースのURLを渡すだけで、そのリソースを非同期に取得できます。`fetch()`関数は、リクエストが成功した場合に解決されるプロミスを返します。

fetch('https://example.com/data')
  .then(response => {
    if (!response.ok) {
        throw new Error('Network response was not ok');
    }
    return response.json(); // JSONデータをJavaScriptオブジェクトにパースする
  })
  .then(data => console.log(data))
  .catch(error => console.error('Fetch error:', error));

特徴

  • プロミスベース: 非同期操作を扱うためのプロミスを使用し、コールバック地獄を避けることができます。XMLHttpRequestはプロミス(Promise)をネイティブにサポートしていません。XHRは古いコールバックベースのアプローチを使用しており、非同期リクエストの結果を処理するためにはイベントリスナーやコールバック関数を設定する必要があります。これは、特に複数の非同期処理を連鎖させる必要がある場合に、コードが複雑になりがちな「コールバック地獄」という問題につながることがあります。

  • リクエスト/レスポンスの制御: ヘッダー、クロスオリジンリクエスト、キャッシュポリシーなど、リクエストとレスポンスの両方を細かく制御できます。

  • ストリーム: レスポンスボディをストリームとして扱うことができるため、巨大なデータを効率的に処理することが可能です。

  • キャンセル可能: AbortControllerを使用して、進行中のフェッチ操作をキャンセルすることができます。

注意点

  • Fetch APIはHTTPエラーステータスを直接的なエラーとして扱わず、ネットワークエラー時にのみプロミスが拒否(reject)されます。したがって、レスポンスのステータスコードを確認し、適切に処理する必要があります。

  • CORS(Cross-Origin Resource Sharing)ポリシーに準拠しており、異なるオリジン間のリクエストはサーバー側で適切なCORSヘッダーを設定する必要があります。

Fetch APIは、現代のWeb開発において非常に重要なツールの一つであり、多くのフロントエンドフレームワークやライブラリでも広く利用されています。

XMLHttpRequestの場合

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/data', true);

xhr.onload = function () {
  if (xhr.status >= 200 && xhr.status < 300) {
    // リクエスト成功
    var data = JSON.parse(xhr.responseText);
    console.log(data);
  } else {
    // サーバーからのエラーレスポンスを処理
    console.error('Request failed with status:', xhr.status);
  }
};

xhr.onerror = function () {
  // ネットワークエラーなどのリクエスト失敗を処理
  console.error('Request failed');
};

xhr.send();

このコードは、指定されたURLからデータを非同期に取得しようと試みます。XMLHttpRequestオブジェクトを作成し、openメソッドでHTTPメソッドとURLを指定してリクエストを初期化します。onloadイベントハンドラーは、リクエストが完了したとき(レスポンスが完全に受信されたとき)に呼び出され、ステータスコードをチェックして成功した場合にレスポンスを処理します。onerrorイベントハンドラーは、リクエストが何らかの理由で失敗したときに呼び出されます。最後に、sendメソッドを使用してリクエストを送信します。

Fetch APIと比較して、XMLHttpRequestはより冗長で、現代のJavaScriptの非同期処理パターン(プロミスやasync/await)との相性がやや悪いです。ただし、古いブラウザのサポートが必要な場合など、まだ有用な場面もあります。


response.body

`response`オブジェクトは、Fetch APIを使用してリソースを非同期に取得する際に得られるオブジェクトです。このオブジェクトは、リクエストに対するレスポンスの詳細情報を含んでおり、HTTPレスポンスステータスコード、レスポンスヘッダー、レスポンスボディなど、レスポンスに関する様々な情報にアクセスできるプロパティとメソッドを提供します。

主なプロパティとメソッド

  • `ok`: レスポンスのステータスコードが200から299の範囲にある場合は`true`を返し、それ以外の場合は`false`を返します。これにより、リクエストが成功したかどうかを簡単に判断できます。

  • `status`: レスポンスのHTTPステータスコード(例: 200, 404, 500)を返します。

  • `statusText`: HTTPステータスコードに対応するテキストメッセージ(例: "OK", "Not Found")を返します。

  • `headers`: レスポンスヘッダーを操作するための`Headers`オブジェクトです。これを使って、特定のレスポンスヘッダーの値を取得したり、ヘッダーが存在するかどうかを確認したりできます。

  • `body`: レスポンスボディのデータにアクセスするための`ReadableStream`オブジェクトです。このストリームを使用して、レスポンスボディを読み込んだり、テキストやJSONなどの形式に変換したりすることができます。

  • `json()`: レスポンスボディをJSONとして解析し、その結果を返すプロミスを返します。

  • `text()`: レスポンスボディをテキストとして読み込み、その結果を返すプロミスを返します。

  • `blob()`: レスポンスボディをBlobとして読み込み、その結果を返すプロミスを返します。

  • `arrayBuffer()`: レスポンスボディを`ArrayBuffer`として読み込み、その結果を返すプロミスを返します。

使用例その1

以下の例では、Fetch APIを使用して非同期にデータを取得し、レスポンスオブジェクトを使用してレスポンスボディをJSON形式で解析しています。

fetch('https://example.com/data')
  .then(response => {
    if (response.ok) {
      return response.json();
    }
    throw new Error('Network response was not ok.');
  })
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error('Fetching error:', error);
  });

この例では、まず`response.ok`をチェックしてリクエストが成功したかどうかを確認しています。成功した場合、`json()`メソッドを呼び出してレスポンスボディをJSONオブジェクトとして解析し、そのデータを使用しています。エラーが発生した場合は、キャッチブロックでエラーメッセージをログに記録します。

ステータスコード

サーバーがクライアントに返すもの。
サーバー側からみた場合、ゼロからサーバープログラムを自分で書くでなければ、おおよそある程度は自動的にやってくれるもの。必要に応じて自分で書くもの。
クライアント側からみた場合、リクエストがサーバーにどのように処理されたかうかがい知るもの。

HTTPステータスコードは、ウェブサーバーからの応答の一部としてクライアント(通常はウェブブラウザやAPIを利用するアプリケーション)に送信される、標準化された数値です。これらのコードは、クライアントのリクエストがどのように処理されたかを示すために使用されます。ステータスコードは、リクエストが成功したか、リダイレクトが必要か、クライアント側にエラーがあるか、サーバー側でエラーが発生したかなど、さまざまな結果を伝えることができます。

ステータスコードのカテゴリー

ステータスコードは以下の5つのカテゴリーに分けられます:

  • 1xx (情報応答): サーバーがリクエストを受け取り、プロセスの続行を通知する。

  • 2xx (成功): リクエストが成功し、サーバーが期待される応答を送信した。

  • 3xx (リダイレクション): 追加のアクションが必要であり、クライアントがリクエストを完了するために他の場所にリダイレクトされる。

  • 4xx (クライアントエラー): リクエストに問題があるため、サーバーがリクエストを処理できない。

  • 5xx (サーバーエラー): サーバーが正常にリクエストを処理することができない状態。

いくつかの一般的なステータスコード

  • 200 OK: リクエストが成功し、応答には要求されたデータが含まれています。

  • 301 Moved Permanently: リクエストされたリソースが恒久的に新しいURLに移動されたことを示します。

  • 404 Not Found: サーバーがリクエストされたリソースを見つけることができない。

  • 500 Internal Server Error: サーバー側で未処理のエラーが発生し、リクエストを処理できない。

重要性

ステータスコードは、ウェブコミュニケーションの重要な部分であり、クライアントとサーバー間の効率的なデータ交換を可能にします。正しいステータスコードを使用することで、開発者はエラーハンドリングを適切に実装でき、ユーザーはより良いウェブ体験を得ることができます。サーバー開発者は、特定のシナリオに対して適切なステータスコードを返すことによって、リクエストの状態を正確に伝えることができます。

使用例その2:HTML、JSON、画像、テキストファイルなど

response.bodyプロパティにそのままアクセスするとReadableStreamオブジェクトを返します。これはバイナリストリームです。このままだと使いづらいので、基本的にはresponse.textやresponse.jsonなどで、ストリームを扱いやすくしてから使用します。

`fetch` APIは非常に柔軟で、HTML、JSON、画像、テキストファイルなど、さまざまな種類のデータを扱うことができます。以下に、それぞれのデータタイプを扱う基本的な例を示します。

HTMLを扱う

HTMLデータをフェッチし、テキストとして取得する例です。

fetch('https://example.com')
  .then(response => response.text())
  .then(html => {
    console.log(html);
    // ここでHTMLをDOMに挿入したり、解析したりすることができます
  })
  .catch(error => console.error('Error loading HTML:', error));

htmlはただのテキスト扱いです。

JSONを扱う

APIからJSONデータをフェッチし、それをJavaScriptオブジェクトとして解析する例です。

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(json => {
    console.log(json);
    // JSONデータを利用した処理をここで行う
  })
  .catch(error => console.error('Error loading JSON:', error));

画像を扱う

画像をフェッチし、ブラウザ上に表示する例です。`Blob`オブジェクトを使用して画像データを扱います。

fetch('https://example.com/image.png')
  .then(response => response.blob())
  .then(blob => {
    const imgURL = URL.createObjectURL(blob);
    const img = document.createElement('img');
    img.src = imgURL;
    document.body.appendChild(img);
    // 画像がDOMに挿入され、表示されます
  })
  .catch(error => console.error('Error loading image:', error));

テキストファイルを扱う

テキストファイルをフェッチし、その内容を読み込む例です。

fetch('https://example.com/data.txt')
  .then(response => response.text())
  .then(text => {
    console.log(text);
    // テキストファイルの内容を利用した処理をここで行う
  })
  .catch(error => console.error('Error loading text file:', error));

これらの例は、`fetch` APIを使用してさまざまなタイプのリソースをフェッチし、適切な方法でレスポンスを処理する基本的な方法を示しています。`fetch` APIの柔軟性により、ウェブアプリケーションで必要とされるほとんどの種類のデータを簡単に取得し、利用することができます。

フォームデータを扱う

formData() メソッドは、レスポンスボディをFormDataオブジェクトとして解析して返します。FormDataオブジェクトは、キーと値のペアを含む、フォームデータを扱うために設計されています。これは特に、フォームの送信やファイルアップロードなど、フォームデータの送受信に関連する操作に有用です。
formData()メソッドが返すFormDataオブジェクトには、フォームに入力されたデータが含まれます。例えば、テキストフィールド、選択肢(セレクトボックス)、チェックボックス、ラジオボタン、ファイル入力などのフォーム要素の値が含まれることがあります。各エントリーはキー(フォーム要素のname属性に対応)と値のペアとして格納されます。
formData()を使用する例を以下に示します。この例では、サーバーからFormData形式のレスポンスが返されることを想定しています。

fetch('https://example.com/formData')
  .then(response => response.formData())
  .then(formData => {
    // FormDataオブジェクトからデータを取得
    for(let key of formData.keys()) {
      console.log(key, formData.get(key));
    }
  })
  .catch(error => console.error('Error:', error));



response.header


fetch('https://example.com/data')
  .then(response => {
    // レスポンスヘッダーにアクセス
    console.log(response.headers.get('Content-Type'));

    // レスポンスボディをJSONとして解析
    return response.json();
  })
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

HTTPレスポンスヘッダーは、クライアントへのレスポンスに関するメタデータを提供します。これには、レスポンスの内容やキャッシングポリシー、セキュリティ関連の情報などが含まれます。以下に、一般的なレスポンスヘッダーの例と、より詳細な情報を含む複雑なレスポンスヘッダーの例を示します。

一般的なレスポンスヘッダーの例

HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 88

この例では、以下の情報が含まれています:

  • `Date`: レスポンスが生成された日時。

  • `Server`: レスポンスを提供するサーバーの情報。

  • `Last-Modified`: コンテンツが最後に変更された日時。

  • `Content-Type`: レスポンスの内容のMIMEタイプと文字セット。

  • `Content-Length`: レスポンスのボディのバイト数。

最大限込み入ったレスポンスヘッダーの例

HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache/2.4.1 (Unix)
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 88
Vary: Accept-Encoding
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
Set-Cookie: UserID=JohnDoe; Max-Age=3600; Secure; HttpOnly
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Content-Security-Policy: default-src 'self'
X-XSS-Protection: 1; mode=block

このより複雑な例では、追加のセキュリティとキャッシングに関するヘッダーが含まれています:

  • `Vary`: キャッシュの決定に使用されるリクエストヘッダー。

  • `Cache-Control`, `Pragma`, `Expires`: リソースのキャッシングポリシーを定義。

  • `Set-Cookie`: クライアントにCookieを設定。

  • `Strict-Transport-Security`: HTTPSを強制するポリシー。

  • `X-Content-Type-Options`: MIMEタイプスニッフィングを防ぐ。

  • `X-Frame-Options`: クリックジャッキング攻撃から保護。

  • `Content-Security-Policy`: コンテンツのセキュリティポリシーを定義。

  • `X-XSS-Protection`: クロスサイトスクリプティング攻撃からの保護。

これらのヘッダーは、セキュリティを強化し、リソースの使用方法を細かく制御するために重要です。サーバーの設定やアプリケーションの要件に応じて、これらのヘッダーを適切に設定することが推奨されます。

ReadableStreamとArrayBuffer

`ReadableStream`と`ArrayBuffer`は、どちらもバイナリデータを扱うための機能ですが、主な違いはデータの読み込み方と使用目的にあります。

ReadableStream

  • `ReadableStream`は、データを時間をかけて少しずつ読み込むためのものです。これは、特に大きなデータセットや、時間をかけてサーバーから送信されるストリーミングデータの処理に適しています。

  • ストリームは非同期で動作し、データの読み込みを非同期に行いながら、読み込まれたデータに対して何らかの処理を行うことができます。これにより、全てのデータが到着するのを待たずに処理を開始できるため、アプリケーションのレスポンスを改善できます。

  • `fetch` APIの`response.body`プロパティを通じて利用可能で、`getReader()`メソッドを使用してデータのチャンクを読み出すことができます。

ArrayBuffer

  • `ArrayBuffer`は、バイナリデータの固定長の生のバッファです。`ArrayBuffer`を使用すると、データ全体を一度にメモリに読み込み、その後でデータを操作します。

  • `ArrayBuffer`は同期的にデータを扱います。データを一度に全て読み込むため、データのサイズが小さいか、すべてのデータが必要になるまで処理を待てる場合に適しています。

  • `fetch` APIの`response.arrayBuffer()`メソッドを使って取得できます。このメソッドはPromiseを返し、Promiseが解決されるとデータ全体が`ArrayBuffer`として利用可能になります。

まとめ

  • `ReadableStream`は、データが大きく、逐次的に処理する必要がある場合や、リアルタイムでデータを受信しながら処理を行いたい場合に適しています。ストリームを使うことで、アプリケーションのパフォーマンスを改善し、ユーザー体験を向上させることができます。

  • `ArrayBuffer`は、データのサイズが管理可能で、データ全体に対して何らかの操作を行いたい場合に適しています。`ArrayBuffer`を使用すると、データをバッファに読み込んだ後で、そのデータをさまざまな形式に変換したり、直接操作したりすることができます。

これらの違いを理解することで、アプリケーションの要件に応じて最適なデータ読み込み方法を選択することができます。

readとthen

`read()`メソッドと`then()`メソッドは、JavaScriptの非同期プログラミングにおける重要な概念です。これらは、特にプロミス(Promise)やストリーム(Streams)を扱う際に頻繁に使用されます。以下、それぞれの機能と用途について詳しく説明します。

`read()`メソッド

`read()`メソッドは、`ReadableStream`のインターフェースの一部です。これは、ストリームからデータを読み込むために使用されます。ストリームとは、時間の経過とともに利用可能になるデータのシーケンスです。Web APIである`fetch()`メソッドからのレスポンスなど、非同期にデータを取得する際にしばしばストリームが用いられます。

`read()`メソッドはプロミスを返し、プロミスが解決されると、読み込まれたデータのチャンク(`value`)と、ストリームの終端に達したかどうかを示すフラグ(`done`)を含むオブジェクトを返します。このメソッドを使用することで、データを小分けにして段階的に処理することができます。

`then()`メソッド

`then()`メソッドは、プロミス(Promise)に基づいた非同期プログラミングを行う際に使用されます。プロミスは、非同期操作の最終的な完了(または失敗)とその結果の値を表します。

`then()`メソッドをプロミスにチェーンすることで、プロミスが「解決」(成功)した場合に実行されるコールバック関数を指定できます。また、オプションとして、プロミスが「拒否」(失敗)した場合に実行される別のコールバック関数も指定できます。

`then()`メソッドは、プロミスの結果に基づいて非同期操作の後続の処理を行うために使用され、プロミスチェーンを形成することで、複数の非同期操作を順序よく、かつ読みやすく管理することができます。

以下のコードスニペットでは、`fetch()`メソッドからのレスポンスストリームを読み込み、結果を段階的に処理する方法を示しています。

fetch('some-url')
  .then(response => {
    const reader = response.body.getReader();
    return reader.read().then(function processText({ done, value }) {
      if (done) {
        console.log("Stream completed");
        return;
      }
      console.log(`Received chunk:`, value);
      // 次のチャンクを読み込む
      return reader.read().then(processText);
    });
  });

この例では、`read()`メソッドでデータのチャンクを非同期に読み込み、`then()`メソッドで読み込みが成功した場合の処理を行っています。再帰的に`processText`関数を呼び出すことで、ストリームからのすべてのデータを読み込んでいます。

この記事が気に入ったらサポートをしてみませんか?