Poni Poni - 1 month ago 10
Javascript Question

JavaScript ES6 promise for loop

for (let i = 0; i < 10; i++) {
const promise = new Promise((resolve, reject) => {
const timeout = Math.random() * 1000;
setTimeout(() => {
console.log(i);
}, timeout);
});

// TODO: Chain this promise to the previous one (maybe without having it running?)
}


The above will give the following random output:

6
9
4
8
5
1
7
2
3
0


The task is simple: Make sure each promise runs only after the other one (
.then()
).

For some reason, I couldn't find a way to do it.

I tried generator functions (
yield
), tried simple functions that return a promise, but at the end of the day it always comes down to the same problem: The loop is synchronous.

With async I'd simply use
async.series()
.

How do you solve it?

Answer

As you already hinted in your question, you cannot do this with a for loop, which is synchronous. Instead create a function that you call again whenever a promise resolves. Don't forget to actually resolve it!

(function loop(i) {
    const promise = new Promise((resolve, reject) => {
        const timeout = Math.random() * 1000;
        setTimeout( () => {
            console.log(i);
            resolve(); // resolve it!
        }, timeout);
    }).then( () => i >= 10 || loop(i+1) );
})(0);

This creates a function named loop, and at the every end of the code you can see it gets called immediately with argument 0. This is the counter, and the i argument.

Whenever the timer expires the pending promise is resolved with resolve(). This will trigger the then callback to be called (asynchronously). That callback will check the counter and call the function again if still required.

Note that you don't actually need to store the promise in the variable promise.