jrevertvila
jrevertvila

Reputation: 58

How to execute promise.then in each iteration of loop? Javascript

I'm having an issue with a promise inside a loop. I have 10000 rows in databse, and I wanna print first 1000 rows and then, fetch 9 requests to print the rest. Technically, I want to print the 1000 results on each iteration of loop, for that, I'm using a promise for get the data, and .THEN create 1000 rows and print it.

But the problem is that first JavaScript's loops get's the 10 requests and then, execute the 10 .THEN together.

I mean:

1000 results  (First loop)
2000 results (Second loop) - Append 1000 new rows to last 1000.
3000 results (Third loop) - Append 1000 new rows to last 2000.
....

What I obtain:

Console.logs example

In the last image, "first for loop" are getting 10 requests and, when the loop finishes, .THEN print each promise callback (.THEN)

That's my code:

fetchData('ArticulosSearch', inputValues).then(function (data) {
        console.log(data);
        var source = getGridSource(url, datafields, "array", data);
        // Creamos el grid con las primeras 1000 rows obtenidas
        $("#GridAseguradora").createGrid(source, dataColumns, params);

        //Obtenemos el nº de registros que tiene la bbdd para calcular cuantas peticiones debe hacer
        setTimeout(() => {
            fetchData('ArticulosSearchCount', inputValues).then(function (total_rows) {
                console.log("count");
                console.log(total_rows);
                var filt = (total_rows[0].Column1 / 1000) //pagines - peticiones
                console.log(filt);

                // Creamos promesa para añadir posteriormente las filas al grid una vez hayan terminado todas las peticiones
                for (var i = 1; i < filt; i++) {
                    (function (i) {
                        console.log("first for loop");
                        inputValues.offset = (1000 * i)
                        fetchData('ArticulosSearch', inputValues).then(function (data) {  //Obtenemos las 1000 siguientes filas y las añadimos a un array
                            console.log("obtain 1000 results");
                            console.log(data);
                            var rows = new Array();

                            data.forEach(el => {
                                rows.push(generaterow(el))
                            });

                            $("#GridAseguradora").jqxGrid('addrow', null, rows); // Agregamos las filas generadas anteriormente al grid
                            $("#GridAseguradora_contRegTotal").text("Reg. Visibles: " + $("#GridAseguradora").jqxGrid('getrows').length)
                        })
                    })(i);
                }
            })
        }, 100);
    })

The Console.logs I wanna obtain:

1-first for loop
obtain 1000 results
(append results)

2-first for loop
obtain 1000 results
(append results)

.....

What I hope is that the client sees that the products are being loaded little by little, not that the remaining 9000 are loaded together

Upvotes: 0

Views: 1356

Answers (1)

Georgi B. Nikolov
Georgi B. Nikolov

Reputation: 998

You are immediately appending the .then() clauses when you run your loop, hence they immediately get resolved. You essentially want to block each loop iteration until the promise resolves and then continue on to the next one and repeat.

You can do this with async / await too, but here is how you achieve synchronous loop iterations with promises:

let promise = Promise.resolve()

for (let i = 0; i < 10; i++) {
  promise = promise.then(() => {
    fetchData('ArticulosSearch', inputValues).then(res => {
       // your logic here
    })
  })
}

You initialise a new promise with Promise.resolve() and keep appending then()s to it on each loop iterations. These .then() will execute only when the previous one is resolved.

This will ensure things are properly scheduled one after another.

Take a look at this snippet, I tried to simulate some async operation that takes variable time to resolve:

const outputEl = document.getElementById('output')

const fetchSomethingFromDBAsyncFake = (idx) => new Promise((resolve, reject) => {
  const timeout = setTimeout(() => {
    outputEl.innerHTML += `
      <br />
      Loaded chunk ${idx}
    `
    resolve()
    clearTimeout(timeout)
  }, 500 + Math.random() * 1750)
})

let promise = Promise.resolve()

for (let i = 0; i < 10; i++) {
  promise = promise.then(() => fetchSomethingFromDBAsyncFake(i))
}
<div id="output"></div>

Upvotes: 2

Related Questions