Aquí está mi código hasta ahora:
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(); }Cuando estaba tratando de resolver este problema hace 2,5 años (!), alguien me dijo que la forma más fácil "es mantener una cadena de promesas para realizar un seguimiento de las solicitudes", así...
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); }La persona que sugirió este código comentó...
Ahora se realizará una solicitud tras otra, y si se alcanza el límite de velocidad, esperará un minuto.
Es posible que desee volver a intentarlo en caso de un error de limitación de velocidad. También puede agregar varias colas de promesa para permitir un mayor rendimiento.
Parece que cuando lo intenté anteriormente, ¿estableció un retraso de 60 segundos antes de realizar llamadas? Creo que me gustaría volver a probar este método, pero no estoy seguro de cómo codificarlo. Por ejemplo, no estoy seguro de cómo const apiCall = requests.then(() => con mi código actual. Puedo ver que el código sugerido en realidad devuelve 'apiCall', mientras que mi método está configurado para devolver todos los campos de datos individuales, por lo que no estoy seguro de cómo proceder allí. Parece un buen método para obtener el Ratelimit de velocidad del host y establecer un tiempo de espera según sea necesario, pero realmente no estoy seguro de por dónde empezar. Cualquier ayuda, por favor ?
Editar: he estado tratando de hacerlo así, pero todavía no funciona:
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);Edit2: Me preguntaba si había un problema en el que comenté esta línea:
const rateLimit = Math.floor((60 / response.headers.get("X-Discogs-Ratelimit-Remaining")) * 1000);
Así que traté de descomentarlo, pero ahora obtengo
Error de referencia no capturado: la
responseno está definida
Edit3: Recibí una sugerencia para que la función createThrottler() funcione: -
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); } } }No puedo decir que alguna vez hubiera resuelto eso por mí mismo, pero ahí estamos. Así que ahora estoy tratando de averiguar cómo y dónde codificar
const rateLimit = Math.floor((60 / response.headers.get("X-Discogs-Ratelimit-Remaining")) * 1000);sin obtener
No capturado (en promesa) ReferenceError: la respuesta no está definida
¿Has mirado el rebote ?
Puede tasar el límite como en 1 llamada atendida en cualquier período definido. Piense en esto como cuantización. La otra forma es contar las llamadas durante un período de tiempo prolongado y luego bloquear más llamadas indefinidamente o por una duración definida; todo se reduce a su caso de uso preferido.
Normalmente, la limitación de velocidad tiene más que ver con la seguridad y la primera opción (1 llamada atendida en un período definido) es apta. Si está haciendo esto para una API web, es posible que desee rechazar las solicitudes que son "demasiado pronto" y brindar al solicitante algún tipo de comentario con un código de estado HTTP apropiado.
Aquí se analiza cómo implementar todas las diferentes opciones: https://thoughtspile.github.io/2018/07/07/rate-limit-promises/
EDITAR: en respuesta al comentario de OP a continuación y al revisar el código ... Creo que lo estás pensando demasiado.
FWIW, uso el rebote en su mayor parte (equivalente a su "acelerador") y se usa literalmente en la línea de rebote (referencia de función, tiempo de espera en milisegundos).
El código se ve así
function debounce(func, waitFor) { let timeout; return (...args) => new Promise(resolve => { if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => resolve(func(...args)), waitFor); }); } Cambie su throttle(fetch) a mi debounce(fetch,2500) y debería ser suficiente. No necesita tener la operación de asignación en esa línea, solo llámela o escriba otra función llamada debouncedFetch para encapsularla y simplemente llámela desde donde lo necesite.
Pruebe el límite de tasa de promesa
de sus documentos:
var throttle = require('promise-ratelimit')(2000); /* rateInMilliseconds */ var startTime = Date.now(); for (var i = 0; i < 10; i++) { throttle().then(function() { console.log(Date.now() - startTime); }); }