6

estoy haciendo forEach en un array pero este bucle tiene dentro una petición ajax por cada repetición y necesito esperar hasta que todos los ajax estén listos para poder continuar a ordernar el array que se va llenando con estas peticiones, este es mi código.

var getTables = (scoreQ, scoreE) => {

    var arr = [];

    Object.keys(scoreQ).forEach(key => {
        var range;
        var scoreDivide = scoreE[key] / 3;
        if (parseFloat(scoreQ[key]) < parseFloat(scoreDivide)) {
            range = "bajo";
        } else if ((parseFloat(scoreQ[key]) >= parseFloat(scoreDivide)) && (parseFloat(scoreQ[key]) <= parseFloat(scoreDivide * 2))) {
            range = "medio";
        } else if ((parseFloat(scoreQ[key]) >= parseFloat(scoreDivide * 2)) && (parseFloat(scoreQ[key]) <= parseFloat(scoreDivide * 3))) {
            range = "alto";
        }

        //console.log(key, range, scoreQ[key], scoreE[key])

        $.ajax({
            type: "post",
            url: "php/resQuery/tables.php",
            data: {
                valuebook: key,
                range
            },
            dataType: "json",
            success: response => {
                var doc = response;
                arr.push(`<tr> <td>${doc.table.valuebook.replace(/_/gi, " ")}</td> <td>${parseFloat(scoreQ[key]).toFixed(1)}</td> <td>${doc.table.rangeT}</td> <td>${doc.table.descriptionp}</td> <td>${doc.table.descriptions}</td> </tr>`);
            }
        });
    })

    arr.sort()
    arr.forEach(element =>{
        console.log(element);
    })

    }

¿cómo puedo hacer esperar el segundo forEach hasta que el primero termine de hacer todas las peticiones?

intenté poner una promesa tipo done y then después del primero pero no funcionó

Emiliano Pamont
  • 1,168
  • 7
  • 28
  • 2
    Puedes setear la opción async:false en tu llamada ajax. De esta forma, se debe completar la llamada para poder continuar con la ejecución del resto del código. – Mario L Sep 13 '18 at 17:05
  • cual es la razón del que quieras esperar a que llegue la la petición anterior? es solo para que tenga el mismo orden en el arreglo arr? – LPZadkiel Sep 13 '18 at 17:10
  • @LPZadkiel Sí así es, entonces omitiría el sort() – Emiliano Pamont Sep 13 '18 at 17:15
  • el problema con `async false` es que detiene tu código y tu página por completo, ya no podrías interactuar con ella mientras espera a que se terminen las ejecuciones ajax, te publicaré una respuesta diferente – LPZadkiel Sep 13 '18 at 17:16
  • con un async await lo puedes hacer te invito a [leer esta respuesta](https://es.stackoverflow.com/a/169324/28035) – JackNavaRow Sep 13 '18 at 18:04
  • @EmilianoPamont edite mi respuesta puedes revisarla a ver si te sirve y me comentas – David Leonardo Molina Ruiz Dav Sep 13 '18 at 18:21

4 Answers4

3

Puedes crear un contador y un setInterval que chequee hasta que el contador sea igual a la longitud de tu primer array. Algo asi:

var getTables = (scoreQ, scoreE) => {

var arr = [];
var cont = Object.keys(scoreQ).length;
var i = 0;
Object.keys(scoreQ).forEach(key => {
    var range;
    var scoreDivide = scoreE[key] / 3;
    if (parseFloat(scoreQ[key]) < parseFloat(scoreDivide)) {
        range = "bajo";
    } else if ((parseFloat(scoreQ[key]) >= parseFloat(scoreDivide)) && (parseFloat(scoreQ[key]) <= parseFloat(scoreDivide * 2))) {
        range = "medio";
    } else if ((parseFloat(scoreQ[key]) >= parseFloat(scoreDivide * 2)) && (parseFloat(scoreQ[key]) <= parseFloat(scoreDivide * 3))) {
        range = "alto";
    }

    //console.log(key, range, scoreQ[key], scoreE[key])

    $.ajax({
        type: "post",
        url: "php/resQuery/tables.php",
        data: {
            valuebook: key,
            range
        },
        dataType: "json",
        success: response => {
            i++;
            var doc = response;
            arr.push(`<tr> <td>${doc.table.valuebook.replace(/_/gi, " ")}</td> <td>${parseFloat(scoreQ[key]).toFixed(1)}</td> <td>${doc.table.rangeT}</td> <td>${doc.table.descriptionp}</td> <td>${doc.table.descriptions}</td> </tr>`);
        }
    });
})
var inter = setInterval(function(){ 
  if (i == cont) {
    arr.sort()
    arr.forEach(element =>{
      console.log(element);
    }) }, 1000);
    clearInterval(inter);
  }
} 
alanfcm
  • 20,427
  • 11
  • 17
  • 34
1

si lo que quieres es que se guarde en el arreglo arr en el mismo orden en el que ejecutas los ajax entonces puedes utilizar los indices del forEach

var getTables = (scoreQ, scoreE) => {

var arr = [];

// función sincrona para lectura de array
var SyncArray = (array, index) => {

}

var ArraydeAjaxs = [];

Object.keys(scoreQ).forEach((key, index) => {
    var range;
    var scoreDivide = scoreE[key] / 3;
    if (parseFloat(scoreQ[key]) < parseFloat(scoreDivide)) {
        range = "bajo";
    } else if ((parseFloat(scoreQ[key]) >= parseFloat(scoreDivide)) && (parseFloat(scoreQ[key]) <= parseFloat(scoreDivide * 2))) {
        range = "medio";
    } else if ((parseFloat(scoreQ[key]) >= parseFloat(scoreDivide * 2)) && (parseFloat(scoreQ[key]) <= parseFloat(scoreDivide * 3))) {
        range = "alto";
    }

    //console.log(key, range, scoreQ[key], scoreE[key])

    ArraydeAjaxs.push(
        $.ajax({
        type: "post",
        url: "php/resQuery/tables.php",
        data: {
            valuebook: key,
            range
        },
        dataType: "json",
        success: response => {
            var doc = response;
            arr[index] = `<tr> <td>${doc.table.valuebook.replace(/_/gi, " ")}</td> <td>${parseFloat(scoreQ[key]).toFixed(1)}</td> <td>${doc.table.rangeT}</td> <td>${doc.table.descriptionp}</td> <td>${doc.table.descriptions}</td> </tr>`;
        }
    });
})
    )

$.when(...ArraydeAjaxs).done(() =>{

    arr.forEach(element =>{
        console.log(element);
    })
});

}
LPZadkiel
  • 2,172
  • 5
  • 10
  • Pero aún así, se irán guardando cómo vayan llegando y el segundo `forEach` se ejecuta al cargar, de todos modos no espera a que halla valores en el `array` cuando se ejecuta el `forEach` simplemente está vacío, aunque usar`async:false` detendrá la ejecución, ordena tal cual y me parece que puedo solucionarlo poniendo una pantalla de carga. – Emiliano Pamont Sep 13 '18 at 17:24
  • claro no consideré el segundo forEach pero resulta que al parecer si debes esperar a que terminen para hacer algo con ese arr en ese caso hay que modificar un poco el código, editaré mi respuesta para darte una opción a este problema – LPZadkiel Sep 13 '18 at 17:43
  • @EmilianoPamont edición de respuesta para que esperen a todas las llamadas ajax y en orden en que se generan las llamadas ajax – LPZadkiel Sep 13 '18 at 18:07
1

Lo ideal es enmascaran las llamadas a ajax con promesas y llamar a Promise.all, que ejecutara el callback hasta que todas estén terminadas, de esta forma no vuelves las llamas ajax bloqueantes para el usuario. Un ejemplo seria:

function getTables(scoreQ, scoreE){
  var promises = [];
  Object.keys(scoreQ).forEach(key => {
    var promise = new Promise((resolve, reject) => {
    // las promesas enmascaran los datos asíncronos que necesitamos
      $.ajax({
        type: "GET",
        url: "https://dog.ceo/api/breeds/image/random",
        dataType: "json",
        success: resolve, //termino de promesa
        error: reject //si ocurre algún error.
      });
    });
    promises.push(promise);
  })

  Promise.all(promises).then((arr) => {
    arr.sort()
    arr.forEach(element =>{
      //aquí se puede insertar ya en el dom
      // element es lo que recibes con cada llamada ajax...
      console.log(element);
    })
  });
}

getTables({a:1, b:2});

Modifique el código inicial para que se pueda ver su funcionamiento al interactuar con otra API. CodePen.

Uriel
  • 501
  • 2
  • 6
1

Cree un pequeño código para que te puedas guiar. Lo primero que hice fue crear una variable item donde almaceno mi array, luego paso ese array como argumento a la función procesarArray donde si te fijas es una función async y dentro esta un for en vez de un forEach, el for llama a un función async llamada promesa el cual es una especie de manejador de promesas y el a su vez llama a un función ajax donde colocarías tu ajax y el retornaria la respuesta a promesa y el a su vez a procesarArray. En procesarArray una vez recibida la respuesta puedes evaluar si se ejecuto bien el ajax o no, este es el código:

var items = [{item: 1, desc: "item1"}, {item: 2, desc: "item2"}, {item: 3, desc: "item3"}];

const ajax = (item) => {
  
  return new Promise(resolve => 
    setTimeout(resolve(item.desc),2000)
  );
  
}

const promesa = async(item) => {
  
  console.log("Llamando al ajax")
  let respuesta = await ajax(item);
  console.log("Ajax respondio = "+respuesta);
  return respuesta;
  
}

const procesarArray = async (items) => {

  for(const item of items){
    let respuesta = await promesa(item)
    console.log(respuesta);
  }
  
  console.log("listo");
  
}

procesarArray(items);