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 шт):
Рекурсивный вызов 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}
/*Если ошибка другая, возвращаем сообщение об неизвестной ошибке*/
}
Рекурсия тут не нужна, она лишь усложняет понимание и поддержку кода
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 };
}
}
}
}