Recursive auto increment function in not resolving

泪湿孤枕 提交于 2021-02-05 08:08:58

问题


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

回答1:


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.



来源:https://stackoverflow.com/questions/61009255/recursive-auto-increment-function-in-not-resolving

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!