Функция рекурсивного автоматического приращения при не разрешении - PullRequest
1 голос
/ 03 апреля 2020

Короче говоря, я использую эту функцию, чтобы получить уникальное имя пользователя для пользователя, но есть вероятность, что оно уже существует - в этом случае оно должно запускаться снова, пока нет пользователя с ним. Проблема в том, что при повторном вызове изнутри функция не разрешается. Сама функция работает (обновляет документ в mongodb), но я не могу получить порядковый номер. Что я делаю неправильно? Любой совет приветствуется.

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

Я вызываю его из другой функции и назначаю его const с помощью async / await, если это важно.

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

1 Ответ

1 голос
/ 03 апреля 2020

Проблема в том, что когда вы рекурсивно вызываете getNextUsername(), вы не разрешаете самое внешнее Обещание. Этот вызов вызовет новое Обещание и (возможно), которое будет разрешено, но значение не будет go нигде, поскольку оно не используется.

Так как getNextUsername() производит Обещание и он (надеюсь) разрешается со значением, вы можете очень просто изменить свой код, добавив шаг разрешения:

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

Таким образом, когда последующее Обещание будет разрешено, вы будете распространять разрешенное значение из Обещания. Независимо от того, сколько у вас обещаний, это должно работать, даже если у вас есть Promise1 -> Promise2 -> Promise3, когда третий разрешается, второй затем вызовет .then(res) и вернет значение, которое снова вызовет .then(res) в первое Promise и pu sh значение out.

.catch(rej) будет делать то же самое, но с ошибками - если какое-либо внутреннее обещание не будет выполнено, все те, что находятся наверху, потерпят неудачу.

Вот очень простая реализация рекурсивных функций с использованием Promises просто для иллюстрации идеи:

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

Последнее замечание: я не знаю, является ли это лучшим способом решения вашей проблемы. Я не знаком с MongoDB, возможно, есть лучшие подходы. Мое решение - просто исправить текущий код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...