Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
794 views
in Technique[技术] by (71.8m points)

javascript - 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
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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);

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...