JavaScript での膨大なデータの API の効率的な使用
大規模なデータセットを処理する API を操作する場合、データ フローを効率的に管理し、ページネーション、レート制限、メモリ使用量などの課題に対処することが重要です。この記事では、JavaScript のネイティブ フェッチ関数を使用して API を使用する方法について説明します。次のような重要なトピックが表示されます:
- 大量のデータの処理: システムに負荷がかかることを避けるために、大規模なデータセットを段階的に取得します。
- ページネーション: Storyblok Content Delivery API を含むほとんどの API はページ内のデータを返します。効率的なデータ取得のためにページネーションを管理する方法を検討します。
- レート制限: API は、悪用を防ぐためにレート制限を課すことがよくあります。これらの制限を検出して処理する方法を見ていきます。
- 再試行後のメカニズム: API が 429 ステータス コード (リクエストが多すぎる) で応答した場合、スムーズなデータを確保するために再試行するまでの待ち時間を示す「再試行後」メカニズムを実装します。取得しています。
- 同時リクエスト: 複数のページを並行して取得すると、プロセスが高速化されます。 JavaScript の Promise.all() を使用して同時リクエストを送信し、パフォーマンスを向上させます。
- メモリ リークの回避: 大規模なデータセットを処理するには、慎重なメモリ管理が必要です。 ジェネレーターのおかげで、データをチャンク単位で処理し、メモリ効率の高い操作を保証します。
Storyblok Content Delivery API を使用してこれらのテクニックを検討し、フェッチを使用して JavaScript でこれらすべての要素を処理する方法を説明します。コードを詳しく見てみましょう。
Storyblok Content Delivery API を使用する際の注意事項
コードの説明に入る前に、考慮すべき Storyblok API の重要な機能をいくつか説明します。
- CV パラメータ: cv (コンテンツ バージョン) パラメータは、キャッシュされたコンテンツを取得します。 cv 値は最初のリクエストで返され、同じキャッシュされたバージョンのコンテンツが確実にフェッチされるように、後続のリクエストで渡す必要があります。
- ページおよびページごとのページネーション: page パラメーターと per_page パラメーターを使用して、各リクエストで返される項目の数を制御し、結果ページを反復処理します。
- 合計ヘッダー: 最初の応答の合計ヘッダーは、利用可能な項目の合計数を示します。これは、フェッチする必要があるデータ ページの数を計算するために不可欠です。
- 処理 429 (レート制限): Storyblok はレート制限を強制します。これらをヒットすると、API は 429 ステータスを返します。 Retry-After ヘッダー (またはデフォルト値) を使用して、リクエストを再試行するまでの待ち時間を確認します。
fetch() を使用して大規模なデータセットを処理する JavaScript サンプル コード
JavaScript のネイティブフェッチ関数を使用してこれらの概念を実装する方法を次に示します。
次のことを考慮してください:
- このスニペットは、例として stories.json という名前の新しいファイルを作成します。ファイルがすでに存在する場合は上書きされます。したがって、同じ名前のファイルが作業ディレクトリにすでにある場合は、コード スニペット内の名前を変更します。
- リクエストは並行して実行されるため、ストーリーの順序は保証されません。たとえば、3 ページ目の応答が 2 番目のリクエストの応答よりも速い場合、ジェネレーターは 2 ページ目のストーリーよりも前に 3 ページ目のストーリーを配信します。
- Bun でスニペットをテストしました :)
import { writeFile, appendFile } from "fs/promises"; // Read access token from Environment const STORYBLOK_ACCESS_TOKEN = process.env.STORYBLOK_ACCESS_TOKEN; // Read access token from Environment const STORYBLOK_VERSION = process.env.STORYBLOK_VERSION; /** * Fetch a single page of data from the API, * with retry logic for rate limits (HTTP 429). */ async function fetchPage(url, page, perPage, cv) { let retryCount = 0; // Max retry attempts const maxRetries = 5; while (retryCount <= maxRetries) { try { const response = await fetch( `${url}&page=${page}&per_page=${perPage}&cv=${cv}`, ); // Handle 429 Too Many Requests (Rate Limit) if (response.status === 429) { // Some APIs provides you the Retry-After in the header // Retry After indicates how long to wait before retrying. // Storyblok uses a fixed window counter (1 second window) const retryAfter = response.headers.get("Retry-After") || 1; console.log(response.headers, `Rate limited on page ${page}. Retrying after ${retryAfter} seconds...`, ); retryCount++; // In the case of rate limit, waiting 1 second is enough. // If not we will wait 2 second at the second tentative, // in order to progressively slow down the retry requests // setTimeout accept millisecond , so we have to use 1000 as multiplier await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000 * retryCount)); continue; } if (!response.ok) { throw new Error( `Failed to fetch page ${page}: HTTP ${response.status}`, ); } const data = await response.json(); // Return the stories data of the current page return data.stories || []; } catch (error) { console.error(`Error fetching page ${page}: ${error.message}`); return []; // Return an empty array if the request fails to not break the flow } } console.error(`Failed to fetch page ${page} after ${maxRetries} attempts`); return []; // If we hit the max retry limit, return an empty array } /** * Fetch all data in parallel, processing pages in batches * as a generators (the reason why we use the `*`) */ async function* fetchAllDataInParallel( url, perPage = 25, numOfParallelRequests = 5, ) { let currentPage = 1; let totalPages = null; // Fetch the first page to get: // - the total entries (the `total` HTTP header) // - the CV for caching (the `cv` atribute in the JSON response payload) const firstResponse = await fetch( `${url}&page=${currentPage}&per_page=${perPage}`, ); if (!firstResponse.ok) { console.log(`${url}&page=${currentPage}&per_page=${perPage}`); console.log(firstResponse); throw new Error(`Failed to fetch data: HTTP ${firstResponse.status}`); } console.timeLog("API", "After first response"); const firstData = await firstResponse.json(); const total = parseInt(firstResponse.headers.get("total"), 10) || 0; totalPages = Math.ceil(total / perPage); // Yield the stories from the first page for (const story of firstData.stories) { yield story; } const cv = firstData.cv; console.log(`Total pages: ${totalPages}`); console.log(`CV parameter for caching: ${cv}`); currentPage++; // Start from the second page now while (currentPage <= totalPages) { // Get the list of pages to fetch in the current batch const pagesToFetch = []; for ( let i = 0; i < numOfParallelRequests && currentPage <= totalPages; i++ ) { pagesToFetch.push(currentPage); currentPage++; } // Fetch the pages in parallel const batchRequests = pagesToFetch.map((page) => fetchPage(url, page, perPage, firstData, cv), ); // Wait for all requests in the batch to complete const batchResults = await Promise.all(batchRequests); console.timeLog("API", `Got ${batchResults.length} response`); // Yield the stories from each batch of requests for (let result of batchResults) { for (const story of result) { yield story; } } console.log(`Fetched pages: ${pagesToFetch.join(", ")}`); } } console.time("API"); const apiUrl = `https://api.storyblok.com/v2/cdn/stories?token=${STORYBLOK_ACCESS_TOKEN}&version=${STORYBLOK_VERSION}`; //const apiUrl = `http://localhost:3000?token=${STORYBLOK_ACCESS_TOKEN}&version=${STORYBLOK_VERSION}`; const stories = fetchAllDataInParallel(apiUrl, 25,7); // Create an empty file (or overwrite if it exists) before appending await writeFile('stories.json', '[', 'utf8'); // Start the JSON array let i = 0; for await (const story of stories) { i++; console.log(story.name); // If it's not the first story, add a comma to separate JSON objects if (i > 1) { await appendFile('stories.json', ',', 'utf8'); } // Append the current story to the file await appendFile('stories.json', JSON.stringify(story, null, 2), 'utf8'); } // Close the JSON array in the file await appendFile('stories.json', ']', 'utf8'); // End the JSON array console.log(`Total Stories: ${i}`);
主要な手順の説明
Storyblok Content Delivery API を使用して効率的かつ信頼性の高い API を使用するための、コード内の重要な手順の内訳を次に示します。
1) 再試行メカニズムによるページの取得 (fetchPage)
この関数は、API からの単一ページのデータの取得を処理します。これには、API がレート制限を超えたことを示す 429 (Too Many Requests) ステータスで応答したときに再試行するロジックが含まれています。
retryAfter 値は、再試行するまでの待機時間を指定します。 setTimeout を使用して次のリクエストを行う前に一時停止し、再試行は最大 5 回に制限されています。
2) 初期ページリクエストと CV パラメータ
最初の API リクエストは、合計ヘッダー (ストーリーの合計数を示す) と cv パラメーター (キャッシュに使用される) を取得するため、非常に重要です。
合計ヘッダーを使用して必要なページの合計数を計算でき、cv パラメーターによりキャッシュされたコンテンツが確実に使用されます。
3) ページネーションの処理
ページネーションは、page および per_page クエリ文字列パラメーターを使用して管理されます。このコードは 1 ページあたり 25 のストーリーを要求し (これは調整できます)、合計ヘッダーは取得する必要があるページ数を計算するのに役立ちます。
このコードは、一度に最大 7 つの並列リクエスト (これは調整可能) のバッチでストーリーをフェッチし、API に負担をかけずにパフォーマンスを向上させます。
4) Promise.all() による同時リクエスト:
プロセスを高速化するために、JavaScript の Promise.all() を使用して複数のページが並行して取得されます。このメソッドは、複数のリクエストを同時に送信し、すべてのリクエストが完了するまで待機します。
並列リクエストの各バッチが完了すると、結果が処理されてストーリーが生成されます。これにより、すべてのデータを一度にメモリにロードすることがなくなり、メモリの消費量が削減されます。
5) 非同期反復によるメモリ管理 (await...of 用):
すべてのデータを配列に収集する代わりに、JavaScript ジェネレーター (関数* および for await...of) を使用して、取得された各ストーリーを処理します。これにより、大規模なデータセットを処理する際のメモリの過負荷が防止されます。
ストーリーを 1 つずつ生成することで、コードの効率性が維持され、メモリ リークが回避されます。
6) レート制限の処理:
API が 429 ステータス コード (レート制限) で応答した場合、スクリプトは retryAfter 値を使用します。その後、指定された時間だけ一時停止してから、リクエストを再試行します。これにより、API レート制限への準拠が保証され、あまりにも多くのリクエストが急速に送信されることが回避されます。
結論
この記事では、ネイティブのフェッチ関数を使用して JavaScript で API を使用する際の重要な考慮事項について説明しました。私は次のことを処理しようとします:
- 大規模なデータセット: ページネーションを使用して大規模なデータセットを取得します。
- ページネーション: page パラメーターと per_page パラメーターを使用してページネーションを管理します。
- レート制限と再試行メカニズム: レート制限を処理し、適切な遅延の後にリクエストを再試行します。
- 同時リクエスト: JavaScript の Promise.all() を使用してページを並行して取得し、データの取得を高速化します。
- メモリ管理: JavaScript ジェネレーター (関数* および for await...of) を使用して、過剰なメモリを消費せずにデータを処理します。
これらの手法を適用すると、スケーラブルで効率的かつメモリ安全な方法で API の使用を処理できます。
お気軽にコメント/フィードバックをお寄せください。
参考文献
- JavaScript ジェネレーター
- JavaScript ランタイムを作成する
- Storyblok コンテンツ配信 API
以上がJavaScript での膨大なデータの API の効率的な使用の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









記事では、JavaScriptライブラリの作成、公開、および維持について説明し、計画、開発、テスト、ドキュメント、およびプロモーション戦略に焦点を当てています。

この記事では、ブラウザでJavaScriptのパフォーマンスを最適化するための戦略について説明し、実行時間の短縮、ページの負荷速度への影響を最小限に抑えることに焦点を当てています。

フロントエンドのサーマルペーパーチケット印刷のためのよくある質問とソリューションフロントエンド開発におけるチケット印刷は、一般的な要件です。しかし、多くの開発者が実装しています...

この記事では、ブラウザ開発者ツールを使用した効果的なJavaScriptデバッグについて説明し、ブレークポイントの設定、コンソールの使用、パフォーマンスの分析に焦点を当てています。

スキルや業界のニーズに応じて、PythonおよびJavaScript開発者には絶対的な給与はありません。 1. Pythonは、データサイエンスと機械学習でさらに支払われる場合があります。 2。JavaScriptは、フロントエンドとフルスタックの開発に大きな需要があり、その給与もかなりです。 3。影響要因には、経験、地理的位置、会社の規模、特定のスキルが含まれます。

この記事では、ソースマップを使用して、元のコードにマッピングすることにより、Minified JavaScriptをデバッグする方法について説明します。ソースマップの有効化、ブレークポイントの設定、Chrome DevtoolsやWebpackなどのツールの使用について説明します。

同じIDを持つ配列要素をJavaScriptの1つのオブジェクトにマージする方法は?データを処理するとき、私たちはしばしば同じIDを持つ必要性に遭遇します...

Console.log出力の違いの根本原因に関する詳細な議論。この記事では、Console.log関数の出力結果の違いをコードの一部で分析し、その背後にある理由を説明します。 �...
