user554481
user554481

Reputation: 2109

Javascript promise stuck in pending state

I'm very new to Javascript and suspect I have a basic mistake. I want to run a loop in order, but I'm using promises, and they're stuck in a "pending" state.

function print(i){
    return new Promise(function(resolve,reject){
      console.log(i);
      resolve();
    });
}

counter = 0;
var sequence = Promise.resolve();

// The real while loop is much more complicated than this
while (counter < 10) {
    sequence = sequence.then(function() {
      print(counter);
    }).then(function() {
      counter += 1;
    });
}

My problem is very similar to this question, except that I still get stuck in the pending state even though I make the call to resolve(). What am I doing wrong?

Upvotes: 4

Views: 4172

Answers (2)

Bergur
Bergur

Reputation: 4057

If you cant use async/await you can use array.reduce to sequentially resolve the promises. In that case I let the print function return a function that we can later invoke.

function print(i) {
  return function() {
      return new Promise(function(resolve,reject){
        console.log(i);
        resolve();
      });
  }
}

We then initialize our empty promiseArray, and our counter

const promiseArray = [];
let counter = 0;

We can now add 10 print functions to our array.

while (counter < 10) {
    promiseArray.push(print(counter));
    counter++;
}

Not that the print function now returns a another function. We haven't invoked that function and we haven't tried to resolve our promises. If the print function would've returned a promise (and not a function) the promise would get resolved, and it wouldn't be sequantially resolved. Since we can't guarantee that print(1) finishes before print(2). It would just start some print calls and resolve in whatever order.

To sequantially resolve the promises we use array reduce that starts with an empty promise, resolves it and then calls the next promise function.

promiseArray.reduce((init,curr) => {
    return init.then(curr);
},Promise.resolve())

A full code snippet would look like this:

function print(i) {
  return function() {
      return new Promise(function(resolve,reject){
        console.log(i);
        resolve();
      });
  }
}

const promiseArray = [];
let counter = 0;
// The real while loop is much more complicated than this
while (counter < 10) {
    promiseArray.push(print(counter));
    counter++;
}

promiseArray.reduce((init,curr) => {
	return init.then(curr);
},Promise.resolve())

Upvotes: 1

intentionally-left-nil
intentionally-left-nil

Reputation: 8274

You have two issues. The simpler one is that you aren't returning the result of print, and therefore you are never waiting for the promise to resolve before continuing.

The bigger issue is that your while loop is trying to combine a synchronous loop with promises. Basically, your while loop pseudocode looks like this:

while counter < 10:
  spawn an async task
end

Since counter is only incremented in your async task, the while loop will never end. More importantly, since your while loop never stops, your async tasks never kick off, as javascript is single threaded.

You can solve this with using await as follows:

function print(i) {
  return new Promise(function(resolve, reject) {
    console.log(i);
    resolve();
  });
}

async function runLoop() {
  let counter = 0;
  while (counter < 10) {
    const result = await print(counter);
    counter += 1;
  }
}

runLoop().then(() => {
  console.log('done');
});

Upvotes: 4

Related Questions