簡體   English   中英

如何知道提取何時結束而不鎖定主體 stream?

[英]How to know when a fetch ends without locking the body stream?

我正在向 API 發出請求,但他們的服務器只允許一定數量的活動連接,所以我想限制正在進行的提取次數。 出於我的目的,只有在 HTTP 響應正文到達客戶端時才完成(不是正在進行)提取。

我想創建一個這樣的抽象:

const fetchLimiter = new FetchLimiter(maxConnections);
fetchLimiter.fetch(url, options); // Returns the same thing as fetch()

這將使事情變得簡單得多,但似乎無法知道其他代碼使用的 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ 何時結束,因為流在讀取時被鎖定。 可以使用ReadableStream.tee()將 stream 分成兩部分,使用一個並將另一個返回給調用者(可能也用它構造一個Response ),但這會降低性能,對嗎?

由於fetch使用 Promise,您可以利用它來制作一個簡單的隊列系統。

這是我以前用於排隊基於 promise 的東西的方法。 它通過創建Promise並將其解析器添加到數組來將項目排隊。 當然,在 Promise 解決之前, await會阻止任何以后的承諾被調用。

當一個完成時,我們所要做的就是抓住下一個解析器並調用它。 promise 解析,然后開始fetch

最好的部分,因為我們實際上並沒有消耗fetch結果,所以不必擔心必須clone或其他任何東西......我們只需將其完整傳遞,以便您可以then使用它或其他東西。

*編輯:由於在 fetch promise 解析后正文仍在流式傳輸,因此我添加了第三個選項,以便您可以傳入正文類型,並讓 FetchLimiter 為您檢索和解析正文。

這些都返回一個 promise 最終用實際內容解決。

這樣你就可以讓 FetchLimiter 為你解析正文。 我這樣做是為了讓它返回一個[response, data]數組,這樣你仍然可以檢查響應代碼、標題等內容。

就此而言,如果您需要執行更復雜的操作,例如根據響應代碼解析主體的不同方法,您甚至可以傳入回調或在該await中使用的其他內容。

例子

我添加了注釋以指示FetchLimiter代碼的開始和結束位置...... rest 只是演示代碼。

它使用了一個使用 setTimeout 的假fetch ,它將在 0.5-1.5 秒之間解析。 它將立即啟動前三個請求,然后活動將被填滿,並等待一個解決。

發生這種情況時,您將看到 promise 已解析的注釋,然后隊列中的下一個 promise 將啟動,然后您將在for循環中看到then from 解析。 then我添加了它,以便您可以看到事件的順序。

 (function() { const fetch = (resource, init) => new Promise((resolve, reject) => { console.log('starting ' + resource); setTimeout(() => { console.log(' - resolving ' + resource); resolve(resource); }, 500 + 1000 * Math.random()); }); function FetchLimiter() { this.queue = []; this.active = 0; this.maxActive = 3; this.fetchFn = fetch; } FetchLimiter.prototype.fetch = async function(resource, init, respType) { // if at max active, enqueue the next request by adding a promise // ahead of it, and putting the resolver in the "queue" array. if (this.active >= this.maxActive) { await new Promise(resolve => { this.queue.push(resolve); // push, adds to end of array }); } this.active++; // increment active once we're about to start the fetch const resp = await this.fetchFn(resource, init); let data; if (['arrayBuffer', 'blob', 'json', 'text', 'formData'].indexOf(respType) >= 0) data = await resp[respType](); this.active--; // decrement active once fetch is done this.checkQueue(); // time to start the next fetch from queue return [resp, data]; // return value from fetch }; FetchLimiter.prototype.checkQueue = function() { if (this.active < this.maxActive && this.queue.length) { // shfit, pulls from start of array. This gives first in, first out. const next = this.queue.shift(); next('resolved'); // resolve promise, value doesn't matter } } const limiter = new FetchLimiter(); for (let i = 0; i < 9; i++) { limiter.fetch('/mypage/' + i).then(x => console.log(' -.then ' + x)); } })();

注意事項:

  • 當 promise 解決時,我不能 100% 確定身體是否仍在流式傳輸……這似乎是您的擔憂。 但是,如果這是一個問題,您可以使用其中一種 Body mixin 方法,例如blobtextjson ,直到正文內容完全解析后才能解決( 請參閱此處

  • 作為一個非常簡單的概念證明,我有意保持它非常簡短(如 15 行實際代碼)。 您希望在生產代碼中添加一些錯誤處理,以便如果由於連接錯誤或其他原因導致fetch拒絕,您仍然會減少活動計數器並開始下一次fetch

  • 當然它也使用async/await語法,因為它更容易閱讀。 如果您需要支持較舊的瀏覽器,您可能希望使用 Promises 重寫或使用 babel 或等效工具進行轉換。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM