node.js, блок try-catch не ловит ошибку в текущей функции, но в функции которая ее вызывает

Я пишу приложение на node.js/express/mongoDB, если кратко описать суть, то сейчас я создаю функцию которая генерирует токен, с помощью которого можно будет проверять почту пользователя. Вот проблемный код:

Caller.js:

/* 
   Функция сreateToken() возвращает обьект: err - в слушае ошибки (error message),
   data - в случае успеха (true), status - в случае ошибки (500, 404...). 
*/
try{
  let {err, data, status} = await createToken(userID);
  if(err) callback(err, null, status); // Передаем информацию об ошибке дальше
  else callback(null, true);  // Всё хорошо, нет ошибок, передаем информацию дальше
}catch(err){
  console.log(err);
}

createToken.js

async function createToken(user_id){
try{ 

  let newToken = new Tokens({ // Создаем обьект токена
     user_id: user_id,
     token_id: generateSomeId() // Генерируем id для токена
  });
  await newToken.save(); // Cохраняем созданный токен в базу данных
  return {err: null, data: true};// Если всё хорошо, возвращаем обьект с пустой ошибкой.

}catch(err){
  if(err && err.code == 11000){ //Если токен с таким id уже существует
     createToken(user_id); //Вызываем эту же функцию ещё раз (рукурсия), чтобы сгенерировать другой id
  }else{
     return {err: 'Something went wrong', data: null, status: 500} 
     /*Если ошибка другая, возвращаем сообщение об неизвестной ошибке*/
  }
}
}

Что данный код должен делать

Для генерации уникальных id для токена я использую uuid4 - он достаточно безопасный, и уникальный, поэтому вероятность возникновения двух идентичных id очень мала. Но даже учитывая это, я сделал обработку такой ошибки - (Если вдруг токен с таким id уже существует) теперь если такой токен уже есть в базе данных, мы вызываем эту же функцию (createToken()) ещё раз, тем самым, сгенерируется новый id токена.

В этом примере вместо uuid4 я использую свою функию для генерации id - generateSomeId(), эта функция имеет только два уникальных значения: ['a', 'b'] - Функция рандомно возвращает значение из этого массива, тоесть может быть только два уникальных токена, если эти два уникальных токена созданы, и мы попытаемся создать третий токен, то мы постоянно будем попадать в блок catch(err) в блок, который вызывает сам себя, тем самым, эта функция будет вызывать себя бесконечно.

Что на самом деле он делает

Так и происходит, но так же есть и ошибка, причину которой я не могу найти: При создании первого токена всё идёт хорошо, токен создается с id: 'a' (например) и сохраняется в базе данных. При создании 2-го токена, если сгенерируется другой id, допустим id: 'b' - тоже всё хорошо, но если сгенерируется токен с id который уже существует в базе данных, то выполнится блок catch(err), блок где вызывается рекурсия (там где мы вызываем функцию createToken()), за тем эта функция опять выполнится сначала, и когда доходит до строки с await newToken.save(); то я получаю ошибку:

TypeError: Cannot destructure property 'err' of '(intermediate value)' as it is undefined.

Эта ошибка возникает в модуле который вызывает функцию createToken(), то есть в Caller.js, в блоке catch(err). Эта ошибка говорит что в возвращаемом обьекте из createToken(), свойство err равно undefined, как и в свойстве data и status.

Вопрос

Я не знаю почему так происходит? Почему при вызове рекурсии createToken() я получаю эту ошибку, которая говорит что свойства возвращаемого обьекта равны undefined? Разве ошибку не должен поймать блок catch(err) в функции createToken()? Как я могу решить данную проблему? Целый день ищу причину но безрезультатно. Буду очень благодарен если кто-то понимает в чем может быть ошибка. Заранее Благодарю !)


Ответы (2 шт):

Автор решения: Anatoly

Рекурсивный вызов createToken(user_id) необходимо делать с await:

if(err && err.code == 11000){ //Если токен с таким id уже существует
     await createToken(user_id); //Вызываем эту же функцию ещё раз (рукурсия), чтобы сгенерировать другой id
  }else{
     return {err: 'Something went wrong', data: null, status: 500} 
     /*Если ошибка другая, возвращаем сообщение об неизвестной ошибке*/
  }
→ Ссылка
Автор решения: nörbörnën

Рекурсия тут не нужна, она лишь усложняет понимание и поддержку кода

async function createToken(user_id) {
  while(1) {
    try {
      const newToken = new Tokens({
        user_id: user_id,
        token_id: generateSomeId()
      });
      await newToken.save();
      return { err: null, data: true };
    } catch (err) {
      if (err.code != 11000) {
        console.error(err);
        return { err: 'Something went wrong', data: null, status: 500 };
      }
    }
  }
}
→ Ссылка