下面是我目前的代码:
const allRows = [];
async function fileToLines(file) {
return new Promise((resolve, reject) => {
reader = new FileReader();
reader.onload = function(e) {
parsedLines = e.target.result.split(/\r|\n|\r\n/);
resolve(parsedLines);
};
reader.readAsText(file);
});
}
document
.getElementById('fileInput')
.addEventListener('change', async function(e) {
var file = e.target.files[0];
if (file != undefined) {
fileToLines(file).then( async id => {
console.log(id)
console.log(parsedLines)
console.log(typeof id);
var idInt = id.map(Number);
var idFiltered = id.filter(function(v){return v!==''});
console.log(idFiltered)
for(let id of idFiltered) {
const row = await getRelease(id);
allRows.push(row);
}
download();
});
}
});
function getRelease(idFiltered) {
return fetch(`https://api.***.com/releases/${idFiltered}`, {
headers: {
'User-Agent': '***/0.1',
},
})
.then(response => response.json())
.then(data => {
if (data.message === 'Release not found.') {
return { error: `Release with ID ${idFiltered} does not exist` };
} else {
const id = data.id;
const delimiter = document.getElementById("delimiter").value || "|";
const artists = data.artists ? data.artists.map(artist => artist.name) : [];
const barcode = data.identifiers.filter(id => id.type === 'Barcode')
.map(barcode => barcode.value);
var formattedBarcode = barcode.join(delimiter);
const country = data.country || 'Unknown';
const genres = data.genres || [];
const formattedGenres = genres.join(delimiter);
const labels = data.labels ? data.labels.map(label => label.name) : [];
const formattedLabels = labels.join(delimiter);
const catno = data.labels ? data.labels.map(catno => catno.catno) : [];
const formattedCatNo = catno.join(delimiter);
const styles = data.styles || [];
const formattedStyles = styles.join(delimiter);
const tracklist = data.tracklist ? data.tracklist
.map(track => track.title) : [];
const formattedTracklist = tracklist.join(delimiter);
const year = data.year || 'Unknown';
const format = data.formats ? data.formats.map(format => format.name) : [];
const qty = data.formats ? data.formats.map(format => format.qty) : [];
const descriptions = data.formats ? data.formats
.map(descriptions => descriptions.descriptions) : [];
const preformattedDescriptions = descriptions.toString()
.replace('"','""').replace(/,/g, ', ');
const formattedDescriptions = '"' + preformattedDescriptions + '"';
return [idFiltered,
artists,
format,
qty,
formattedDescriptions,
formattedLabels,
formattedCatNo,
country,
year,
formattedGenres,
formattedStyles,
formattedBarcode,
formattedTracklist
];
}
});
}
function download() {
const ROW_NAMES = [
"release_id",
"artist",
"format",
"qty",
"format descriptions",
"label",
"catno",
"country",
"year",
"genres",
"styles",
"barcode",
"tracklist"
];
var csvContent = "data:text/csv;charset=utf-8,"
+ ROW_NAMES + "\n" + allRows.map(e => e.join(",")).join("\n");
console.log(csvContent);
var encodedUri = encodeURI(csvContent);
var link = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", "my_data.csv");
document.body.appendChild(link); // Required for FF
link.click();
}
当我之前试图破解这个问题2. 5年前(!)有人告诉我最简单的方法“是保持一系列的承诺来跟踪请求”,就像这样...
const timer = ms => new Promise(resolve => setTimeout(resolve, ms));
let requests = Promise.resolve();
function getRelease(id) {
const apiCall = requests.then(() =>
fetch(`https://api.***.com/releases/${id}`, {
headers: {
'User-Agent': '***/0.1',
}
})
);
// add to chain / queue
requests = apiCall.then(response =>
+response.headers.get("X-***-Ratelimit-Remaining") <= 1 && timer(60 * 1000)
);
return apiCall
.then(response => response.json())
.then(parseReleaseData);
}
建议这段代码的人评论说...
现在,一个请求将在另一个请求之后完成,如果达到速率限制,它将等待一分钟。
如果出现速率限制错误,您可能需要重试。您也可以添加多个承诺队列以获得更高的吞吐量。
我以前尝试过这个方法,但是它在调用之前设置了60秒的延迟。我想我应该再尝试一次这个方法,但是我不确定该如何编写它。比如,我不确定const apiCall = requests.then(() =>
是否适合我当前的代码。我可以看到建议的代码实际上返回了“apiCall”,而我的方法设置为返回所有单独的数据字段,所以我不知道如何继续。从主机获取Ratelimit
并根据需要设置超时似乎是一个好方法,但我不知道从哪里开始。有什么帮助吗?
编辑:我一直尝试这样做,但还是不行:
const timer = ms => new Promise(resolve => setTimeout(resolve, ms));
const createThrottler = (rateLimit) => {
let requestTimestamp = 0;
return (requestHandler) => {
return async (...params) => {
const currentTimestamp = Math.floor(Date.now() / 1000);
if (currentTimestamp < requestTimestamp + rateLimit) {
await timer(rateLimit - (currentTimestamp - requestTimestamp))
}
requestTimestamp = Math.floor(Date.now() / 1000);
return await requestHandler(...params);
}
}
}
const throttle = createThrottler(2500);
const throttleFetch = throttle(fetch);
编辑2:我想知道是否有一个问题,我有这一行注解掉:const rateLimit = Math.floor((60 / response.headers.get("X-Discogs-Ratelimit-Remaining")) * 1000);
所以我试着不加评论但现在
未捕获引用错误:未定义response
编辑3:我得到了一个让createThrottler()
函数工作的建议:-
const rateLimit = 2500;
const timer = ms => new Promise(resolve => setTimeout(resolve, ms));
const createThrottler = (rateLimit) => {
let requestTimestamp = 0;
return (requestHandler) => {
return async (...params) => {
const currentTimestamp = Number(Date.now());
if (currentTimestamp < requestTimestamp + rateLimit) {
const timeOut = rateLimit - (currentTimestamp - requestTimestamp);
requestTimestamp = Number(Date.now()) + timeOut;
await timer(timeOut)
}
requestTimestamp = Number(Date.now());
return await requestHandler(...params);
}
}
}
我不能说我曾经为自己解决过这个问题,但是我们已经解决了。所以现在我正在努力解决如何以及在哪里编码
const rateLimit = Math.floor((60 / response.headers.get("X-Discogs-Ratelimit-Remaining")) * 1000);
而不需要
未捕获(在承诺中)引用错误:未定义响应
3条答案
按热度按时间zxlwwiss1#
你看过去抖了吗?
你可以将费率限制为在任何规定的时间段内只处理一次呼叫。可以将此视为量化。另一种方法是在一个延长的时间段内对呼叫进行计数,然后无限期地或在一个规定的持续时间内阻止更多的呼叫-这取决于你的首选用例。
通常速率限制与安全性有更多的关系,第一个选项(在定义的时间段内服务1个呼叫)是合适的。如果您对Web API这样做,您可能希望拒绝“太快”的请求,并给予请求者一些类型的反馈,以及适当的HTTP状态代码。
下面讨论如何实现所有不同的选项:https://thoughtspile.github.io/2018/07/07/rate-limit-promises/
**EDIT:**回应下面的OP注解并回顾代码...我认为您想太多了。
FWIW我在大部分情况下都使用去抖动(相当于你的“节流阀”),它的字面意思是沿着去抖动的路线使用(functionReference,timeoutInMilliseconds)。
代码如下所示
把你的
throttle(fetch)
改成我的debounce(fetch,2500)
就足够了,你不需要在那一行有赋值操作,只需要调用它,或者写另一个函数debouncedFetch
来封装它,然后在你需要的地方调用它。bxpogfeg2#
我尝试使用与OP相同的API端点来解决一个非常类似的问题。我的解决方案获得了一组发布ID的完整发布信息。我怀疑我的解决方案是否接近最优,但它工作正常。
avwztpqn3#
尝试promise-ratelimit
从他们的文档: