Tengo una matriz con casi 2 millones de idSender de Facebook, y quiero iterar sobre ella, llamando a una API de Facebook para cada uno de ellos. Ahora, debido a la asincronía, si lanzo un bucle for simple, Facebook me devolverá un error de límite de velocidad superior después de 4-5 minutos. Después de algunos intentos descubrí que el retraso ideal para evitar el límite de velocidad es de 20 milisegundos.

Así que probé esta solución: envolví mi función en una Promesa y usé setTimeout para establecer un retraso.

async function asyncLoop(values) {
  var prm = await new Promise(function(resolve, reject) {
    setTimeout(function() {
        for (var i=0; i<values.length; i++) {
            check(values[i].idSender, values[i].iscrizione, values[i].id_sito)
            if(checked == values.length) {
                resolve()
            }
        }
    },20);
  });
  return prm;
}

asyncLoop(idSenderArray)

Pero esta solución no funciona, y también sé que casi nunca es una buena idea usar la función setTimeout para manipular la asincronía en Javascript, de hecho, ni siquiera puedo decir qué está sucediendo exactamente allí.

¿Alguna sugerencia para posibles modificaciones a mi función? ¿Alguna idea sobre una implementación totalmente nueva que podría funcionar? Gracias

1
frabis 4 mar. 2018 a las 20:45

3 respuestas

La mejor respuesta

Solo await una vez dentro del ciclo for:

const timer = ms => new Promise(res => setTimeout(res, ms));

async function asyncLoop(values) {
  for (var i = 0; i < values.length; i++) {
    await timer(20);
    check(values[i].idSender, values[i].iscrizione, values[i].id_sito)
  }
}
2
Jonas Wilms 4 mar. 2018 a las 17:48

También puede hacer algo como esto con promesas;

var apiCall = (url,n) => new Promise((v,x) => setTimeout(v,50,`the result of API call @ ${url} no ${n}`)),
    calls   = Array(20).fill()
                       .map((_,i) => new Promise((v,x) => setTimeout(v,20*i))
                                         .then(_ => apiCall("http://facebook.com",i))
                                         .then(v => console.log(`I am doing something with ${v}`)));
.as-console-wrapper{
height: 100%;
max-height: 100% !important
}
0
Redu 4 mar. 2018 a las 18:49

Alternativamente, también se puede usar setTimeout con promesas:

async function asyncLoop(values) {
    let apiCallPromises = values.map((value, index)=> new Promise(resolve=>{
        setTimeout(_=>{
            check(value.idSender, value.iscrizione, value.id_sito)
            resolve()
        }, index*20)
    }))

    return Promise.all(apiCallPromises);
}

Bastante directo, asigna cada valor a una llamada check() con un retraso de 20n ms para cada solicitud posterior.

0
Slava Knyazev 4 mar. 2018 a las 18:58