简体   繁体   中英

Recursive auto increment function in not resolving

Long story short I am using this function to get an unique username for a user, but there's a possibility that it already exists - in this case it should run again until there's no user with it. The problem is that when called again from inside, the function doesn't resolve. The function itself is working (it's updating the document in mongodb) but I can't get the sequence number. What am I doing wrong? Any advice is appreciated.

const getNextUsername = () => {
  return new Promise((res, rej) => {
    Counter.findByIdAndUpdate('usernames', {
      $inc: {
        sequence: 1
      }
    }, {
      upsert: true,
      new: true
    }, (err, result) => {
      if (err) rej(err);

      User.findOne({
        username: result.sequence
      }, (err, u) => {
        if (err) rej(err);

        if (u) {
          // if user with same username exists call the function again
          // and here something fails
          getNextUsername();
        } else {
          res(result.sequence);
        }
      })
    })
  })
}

I am calling it from another function and assigning it to a const with async/await if that's important.

  const autoIncremented = await getNextUsername();
  console.log(autoIncremented); // this actually never logs

The problem is that when you recursively call getNextUsername() you are not resolving the outer-most Promise. That call will spawn a new Promise and (maybe) that will be resolved but the value will go nowhere as it's not used.

Since getNextUsername() does produce a Promise and it (hopefully) resolves with a value, you can very simply amend your code by adding the resolution step:

const getNextUsername = () => {
  return new Promise((res, rej) => {
    Counter.findByIdAndUpdate('usernames', {
      $inc: {
        sequence: 1
      }
    }, {
      upsert: true,
      new: true
    }, (err, result) => {
      if (err) rej(err);

      User.findOne({
        username: result.sequence
      }, (err, u) => {
        if (err) rej(err);

        if (u) {
          //after the recursive call completes, resolve with the returned value or fail if it failed
          getNextUsername()
            .then(res)
            .catch(rej); 
        } else {
          res(result.sequence);
        }
      })
    })
  })
}

This way, when the subsequent Promise is resolved, you'd propagate the resolved value out of the Promise. Regardless of how many promises down you are, this should work even if you have Promise1 -> Promise2 -> Promise3 when the third one resolves, the second will then call .then(res) and return the value, which will again call .then(res) in the first Promise and push the value out.

The .catch(rej) will do a similar thing but with errors - if any inner promise fails, all the ones to the top will fail.

Here is a very simple implementation of recursive functions using Promises just to illustrate the idea:

 function recursiveSum(a, b, debugPrefix = ">") { return new Promise((res, rej) => { console.log(debugPrefix, "recursively summing", a, b); if (b <= 0) { console.log(debugPrefix, "result found"); res(a); } else { console.log(debugPrefix, "going deeper"); recursiveSum(a+1, b-1, debugPrefix + ">").then(res); } }) } recursiveSum(5, 3).then(console.log);

Final note: I don't know if this is the best way to solve your problem. I'm not familiar with MongoDB, perhaps there are better approaches. My solution is to simply fix the current code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM