javascript - node - Realice varias solicitudes a una API que solo puede manejar 20 solicitudes por minuto
node js api (2)
Puede enviar 1 bloque de 20 solicitudes cada minuto o espaciarlas 1 solicitud cada 3 segundos (este último probablemente sea el preferido por los propietarios de la API).
function rateLimitedRequests(array, chunkSize) {
var delay = 3000 * chunkSize;
var remaining = array.length;
var promises = [];
var addPromises = function(newPromises) {
Array.prototype.push.apply(promises, newPromises);
if (remaining -= newPromises.length == 0) {
Promise.all(promises).then((data) => {
... // do your thing
});
}
};
(function request() {
addPromises(array.splice(0, chunkSize).map(apiFetch));
if (array.length) {
setTimeout(request, delay);
}
})();
}
Para llamar al 1 cada 3 segundos:
rateLimitedRequests(bigArray, 1);
O 20 por minuto:
rateLimitedRequests(bigArray, 20);
Si prefiere usar
_.chunk
y
1
_.debounce
_.throttle
:
function rateLimitedRequests(array, chunkSize) {
var delay = 3000 * chunkSize;
var remaining = array.length;
var promises = [];
var addPromises = function(newPromises) {
Array.prototype.push.apply(promises, newPromises);
if (remaining -= newPromises.length == 0) {
Promise.all(promises).then((data) => {
... // do your thing
});
}
};
var chunks = _.chunk(array, chunkSize);
var throttledFn = _.throttle(function() {
addPromises(chunks.pop().map(apiFetch));
}, delay, {leading: true});
for (var i = 0; i < chunks.length; i++) {
throttledFn();
}
}
1
Probablemente desee
_.throttle
ya que ejecuta cada llamada de función después de un retraso, mientras que
_.debounce
agrupa varias llamadas en una sola llamada.
Ver este
article
vinculado desde los
docs
Debounce : Piense en ello como "agrupar múltiples eventos en uno". Imagina que vas a casa, entras en el ascensor, las puertas se están cerrando ... y de repente tu vecino aparece en el pasillo e intenta saltar en el ascensor. ¡Ser cortés! y abre las puertas para él: estás renunciando a la salida del ascensor. Tenga en cuenta que la misma situación puede volver a ocurrir con una tercera persona, y así sucesivamente ... probablemente retrasando la partida varios minutos.
Acelerador : Piense en ello como una válvula, regula el flujo de las ejecuciones. Podemos determinar el número máximo de veces que se puede llamar a una función en un tiempo determinado. Entonces, en la analogía del elevador ... eres lo suficientemente educado como para dejar que la gente entre por 10 segundos, pero una vez que pasa ese retraso, ¡debes irte!
Tengo un método que devuelve una promesa e internamente ese método hace una llamada a una API que solo puede tener 20 solicitudes por minuto. El problema es que tengo una gran variedad de objetos (alrededor de 300) y me gustaría hacer una llamada a la API para cada uno de ellos.
Por el momento tengo el siguiente código:
const bigArray = [.....];
Promise.all(bigArray.map(apiFetch)).then((data) => {
...
});
Pero no maneja la restricción de tiempo.
Esperaba poder usar algo como _.chunk y _.debounce de
lodash
pero no puedo entenderlo.
Podría alguien ayudarme ?
Si puede usar la biblioteca de promesa de Bluebird, tiene una característica de concurrencia integrada que le permite administrar un grupo de operaciones asíncronas como máximo N en vuelo a la vez.
var Promise = require(''bluebird'');
const bigArray = [....];
Promise.map(bigArray, apiFetch, {concurrency: 20}).then(function(data) {
// all done here
});
Lo bueno de esta interfaz es que mantendrá 20 solicitudes en vuelo. Comenzará 20, luego cada vez que uno termine, comenzará otro. Entonces, esto es potencialmente más eficiente que enviar 20, esperando que todos terminen, enviando 20 más, etc.
Esto también proporciona los resultados en el mismo orden exacto que
bigArray
para que pueda identificar qué resultado va con cada solicitud.
Podría, por supuesto, codificar esto usted mismo con promesas genéricas usando un contador, pero como ya está integrado en la biblioteca Bluebird, pensé en recomendarlo de esa manera.
La biblioteca Async también tiene un control de concurrencia similar, aunque obviamente no está basado en promesas.
Aquí hay una versión codificada a mano que usa solo promesas de ES6 que mantiene el orden de los resultados y mantiene 20 solicitudes en vuelo en todo momento (hasta que no queden 20) para un rendimiento máximo:
function pMap(array, fn, limit) {
return new Promise(function(resolve, reject) {
var index = 0, cnt = 0, stop = false, results = new Array(array.length);
function run() {
while (!stop && index < array.length && cnt < limit) {
(function(i) {
++cnt;
++index;
fn(array[i]).then(function(data) {
results[i] = data;
--cnt;
// see if we are done or should run more requests
if (cnt === 0 && index === array.length) {
resolve(results);
} else {
run();
}
}, function(err) {
// set stop flag so no more requests will be sent
stop = true;
--cnt;
reject(err);
});
})(index);
}
}
run();
});
}
pMap(bigArray, apiFetch, 20).then(function(data) {
// all done here
}, function(err) {
// error here
});
Demostración de trabajo aquí: http://jsfiddle.net/jfriend00/v98735uu/