Mojimi
Mojimi

Reputation: 3151

Retry promise until resolved (Too much recursion error)

I was trying to test if I could have a promise retry until it resolved, but it keeps giving "too much recursion" error and I can't understand why it doesn't stop after the 3rd recursion.

The following code is trying to emulate a failed query request to a server, which comes from a non-promise function.

function nonPromiseCallback(x, resolve, reject){    
  if(x < 3){
    reject(x)
  }else{
    resolve(x)
  }
}

function tryUntilThree(x){
  return new Promise( (resolve, reject) => {
    nonPromiseCallback(x, resolve, tryUntilThree(x+1));
  })
}

tryUntilThree(1)
.then(console.log);

Upvotes: 0

Views: 189

Answers (4)

some
some

Reputation: 49582

As I said in my comment, to your question, you had an infinite loop since to call nonPromiseCallback the result of tryUntilThree was needed... and to get that, tryUntilThree was called... and it was looping until memory was exhausted or the end of the universe, whichever came first.

You need to make two changes:

function nonPromiseCallback(x, resolve, reject){
  if(x < 3){
    reject(x+1) // the increment of x is moved to here.
  }else{
    resolve(x)
  }
}

function tryUntilThree(x){
  return new Promise( (resolve, reject) => {
    nonPromiseCallback(x, resolve, tryUntilThree); // Just pass the function, it will be called later
  })
}

tryUntilThree(1)
.then(console.log);

If you can use async and await (new features since 2017) you could solve it like this (I moved the decision of maximum number of tries to the calling function) :

function nonPromiseCallback(x, resolve, reject){
  if(x < 3){
    reject(x+1)
  }else{
    resolve(x)
  }
}

async function tryUntilThree(){
  const maxAttempts = 3;
  let attempt = 1;

  do {
    try {
      // "await" waits for the prommise to resolve or reject.
      // If it is rejected, an error will be thrown. That's why
      // this part of the code is inside a try/catch-block.
      const result = await new Promise( (resolve, reject) =>
        nonPromiseCallback( attempt, resolve, reject )
      );
      return result; // Return the result from nonPromiseCallback
    }
    catch (error) {
      // The nonPromiseCallback failed. Try again
      attempt += 1;
    }
  } while ( attempt <= maxAttempts );

  // Signal error after all retires.
  throw Error(`Failure, even after ${maxAttempts} tries`);
}

tryUntilThree()
.then(console.log);

Upvotes: 1

vicbyte
vicbyte

Reputation: 3790

Since you are interested in a promise failure, you could use the catch handler.

As to why your code doesn't work, heres a good explaination by some (also in the comment):

You get too much recursion because tryUntilThree is called too many times. Notice that you have written tryUntilThree(x+1), ie the engine has to resolve the call to tryUntilThree before it can call nonPromiseCallback. You have an infinite loop there.

function nonPromiseCallback(x, resolve, reject){    
  if(x < 3){
    reject(x)
  }else{
    resolve(x)
  }
}

function tryUntilThree(x){
  return new Promise( (resolve, reject) => 
    nonPromiseCallback(x, resolve, reject)
  ).catch(() => 
    tryUntilThree(x + 1)
  )
}

tryUntilThree(1)
.then(console.log);

Upvotes: 3

robbannn
robbannn

Reputation: 5013

I tried your code but got the TypeError: reject is not a function. That is because you pass in tryUntilThree(x+1) which executes the function before passing it to nonPromiseCallback.

So i came up with this code, to try to accomplish what you want.

let res;    // used for keeping a reference to the original resolve
function nonPromiseCallback(x, resolve, reject){
    if(x < 3){
      reject(x + 1);
    }
    else{
      resolve(x);
    }
}

function tryUntilThree(x){
    return new Promise((resolve) => {
        if(!res){
            res = resolve;
        }
        nonPromiseCallback(x, res, tryUntilThree);
    });
}

tryUntilThree(1)
.then(res => console.log("RESULT:", res));

let res;, this variable is used for keeping a reference to the original resolve, so that the .then executes.

Upvotes: 1

Danyal Imran
Danyal Imran

Reputation: 2605

The problem is with the nonPromiseCallback method call, instead of a function reference, you are passing an actual function and then calling that function.

Problem:

nonPromiseCallback(x, resolve, tryUntilThree(x+1));

Fix:

nonPromiseCallback(x, resolve, tryUntilThree);

and

reject(x+1);

Upvotes: 1

Related Questions