JavaScript 中海量資料的高效能 API 消耗
使用處理大型資料集的 API 時,有效管理資料流並解決分頁、速率限制和記憶體使用等挑戰至關重要。在本文中,我們將介紹如何使用 JavaScript 的本機 fetch 函數來使用 API。我們將看到重要的主題,例如:
- 處理大量資料:增量檢索大型資料集以避免系統不堪負荷。
- 分頁:大多數 API(包括 Storyblok Content Delivery API)以頁面形式傳回資料。我們將探索如何管理分頁以實現高效的資料檢索。
- 速率限制:API 通常會施加速率限制以防止濫用。我們將了解如何檢測和處理這些限制。
- Retry-After機制:如果API回應429狀態碼(請求過多),我們將實作「Retry-After」機制,指示重試之前需要等待多長時間,以確保資料流暢正在取得。
- 並發請求:並行取得多個頁面可以加快進程。我們將使用 JavaScript 的 Promise.all() 發送並發請求並提高效能。
- 避免記憶體洩漏:處理大型資料集需要仔細的記憶體管理。借助生成器。 ,我們將分塊處理資料並確保記憶體高效操作
我們將使用 Storyblok Content Delivery API 來探索這些技術,並解釋如何使用 fetch 在 JavaScript 中處理所有這些因素。讓我們深入研究程式碼。
使用 Storyblok Content Delivery API 時要記住的事項
在深入研究程式碼之前,請先考慮以下 Storyblok API 的一些關鍵功能:
- CV 參數:cv(內容版本)參數檢索快取的內容。 cv 值在第一個請求中返回,並應在後續請求中傳遞,以確保獲取內容的相同快取版本。
- 分頁和每頁分頁:使用 page 和 per_page 參數來控制每個請求中傳回的項目數並迭代結果頁面。
- 總標題:第一個回應的總標題指示可用項目的總數。這對於計算需要獲取多少數據頁至關重要。
- 處理 429(速率限制):Storyblok 強制執行速率限制;當您點擊它們時,API 會傳回 429 狀態。使用 Retry-After 標頭(或預設值)了解重試請求之前需要等待多久。
使用 fetch() 處理大型資料集的 JavaScript 範例程式碼
以下是我如何使用 JavaScript 中的本機 fetch 函數來實現這些概念。
考慮一下:
- 此程式碼片段建立一個名為 Stories.json 的新檔案作為範例。如果該檔案已經存在,它將被覆蓋。因此,如果工作目錄中已有具有該名稱的文件,請更改程式碼片段中的名稱。
- 由於請求是並行執行的,因此無法保證故事的順序。例如,如果第三頁的回應比第二個請求的回應快,則生成器將在第二頁的故事之前傳送第三頁的故事。
- 我用 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(請求過多)狀態時重試的邏輯,這表示已超出速率限制。
retryAfter 值指定在重試之前等待的時間。我在發出後續請求之前使用 setTimeout 暫停,並且重試次數最多限制為 5 次。
2) 初始頁請求與 CV 參數
第一個 API 請求至關重要,因為它會檢索總標頭(指示故事總數)和 cv 參數(用於快取)。
您可以使用total header來計算所需的總頁數,cv參數確保使用快取的內容。
3) 處理分頁
分頁是使用 page 和 per_page 查詢字串參數來管理的。程式碼請求每頁 25 個故事(您可以調整此值),總標題有助於計算需要取得的頁面數。
該程式碼一次最多可批量獲取 7 個(您可以調整此)並行請求的故事,以提高效能,而不會壓垮 API。
4) 使用 Promise.all() 的並發請求:
為了加快流程,使用 JavaScript 的 Promise.all() 並行取得多個頁面。此方法同時發送多個請求並等待所有請求完成。
每批並行請求完成後,將處理結果以產生故事。這樣可以避免一次將所有資料載入到記憶體中,從而減少記憶體消耗。
5) 非同步迭代的記憶體管理(用於await...of):
我們沒有將所有資料收集到數組中,而是使用 JavaScript 生成器(function* 和 wait...of)來處理獲取的每個故事。這可以防止處理大型資料集時出現記憶體過載。
透過一一產生故事,程式碼保持高效並避免記憶體洩漏。
6) 速率限制處理:
如果 API 以 429 狀態代碼(速率受限)回應,則腳本使用 retryAfter 值。然後,它會暫停指定的時間,然後重試請求。這可確保符合 API 速率限制並避免過快發送過多請求。
結論
在本文中,我們介紹了使用本機 fetch 函數在 JavaScript 中使用 API 時的關鍵注意事項。我嘗試處理:
- 大型資料集:使用分頁取得大型資料集。
- 分頁:使用 page 和 per_page 參數管理分頁。
- 速率限制和重試機制:處理速率限制並在適當的延遲後重試請求。
- 並發請求:使用 JavaScript 的 Promise.all() 並行取得頁面,以加快資料擷取速度。
- 記憶體管理:使用JavaScript產生器(function*和await...of)來處理資料而不消耗過多的記憶體。
透過應用這些技術,您可以以可擴展、高效且記憶體安全的方式處理 API 消耗。
請隨時發表您的評論/回饋。
參考
- JavaScript 生成器
- 建置 JavaScript 執行時期
- Storyblok 內容傳遞 API
以上是JavaScript 中海量資料的高效能 API 消耗的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript不需要安裝,因為它已內置於現代瀏覽器中。你只需文本編輯器和瀏覽器即可開始使用。 1)在瀏覽器環境中,通過標籤嵌入HTML文件中運行。 2)在Node.js環境中,下載並安裝Node.js後,通過命令行運行JavaScript文件。
