Итак, я делаю дискорд-бота. Для простоты вот небольшая часть очень, которая иллюстрирует мою проблему:
const Discord = require('discord.js');
const client = new Discord.Client();
client.on('ready', async () => {
throw new Error('Omg');
});
async function start() {
try {
await client.login(process.env.DISCORD_BOT_TOKEN);
} catch (err) {
console.error('Caught the promise rejections');
}
}
start();
Когда я запускаю этот код, я ожидаю, что вывод будет Caught the promise rejections, и процесс должен впоследствии завершиться. Тем не менее, это не так. Вместо этого я получаю PromiseRejectionWarning, и процесс не завершается (для этого мне нужно нажать Ctrl-C). Сначала я подумал, что, возможно, ошибки в обратных вызовах не распространяются на код, который их вызывает, поэтому я сделал еще один чистый пример JS:
const client = {
on(event, callback) {
this.callback = callback;
},
async login(token) {
while (true) {
// I assume the login method calls the callback in D.js
// (where else could it be called?)
await this.callback();
await sleep(5000);
}
},
};
client.on('ready', async () => {
throw new Error('Omg');
});
async function start() {
try {
await client.login(process.env.DISCORD_BOT_TOKEN);
} catch (err) {
console.error('Caught the promise rejections');
}
}
start();
Однако в этом случае результат точно такой, как ожидалось; Вижу строчку от улова и процесс сразу завершается. Без улова я получаю необработанные ошибки отклонения обещания и незавершенный процесс.
Итак, мой вопрос: почему я не могу поймать отказы обещаний в моих обратных вызовах событий (например, on('ready'))?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Причина в том, что ваш второй код не работает как эмиттер событий разногласий, как и Node.js, встроенный в EventEmiter.
Функция обратного вызова для события ready не выполняется с await, и к ней не привязан обработчик .catch, поэтому вы получаете UnhandledPromiseRejectionWarning.
При использовании async в обратном вызове EventEmitter вы должны обработать ошибку, если вы этого не сделаете, вы получите предупреждение, потому что никакой другой код не обрабатывает ее.
client.on('ready', async () => {
try {
throw new Error('Omg');
} catch(e) {
}
});
В вашем конкретном случае кажется, что вы хотите вызвать ошибку, если какое-то условие выполнено на «готово». Вместо этого вы должны обернуть слушателя в Promise.
function discordReady(client) {
return new Promise((resolve, reject) => {
client.once('ready', async () => {
reject(new Error('Omg'));
// resolve..
});
})
}
async function start() {
try {
await Promise.all([
discordReady(client),
client.login(process.env.DISCORD_BOT_TOKEN),
]);
} catch (err) {
console.error('Caught the promise rejections');
}
}
Это даст вам ожидаемое поведение
Также вы бы порекомендовали использовать второй упомянутый вами подход или просто ловить ошибки в каждом отдельном обработчике ошибок?
Я говорю, что если вы не поймаете ошибку внутри обратного вызова on('ready'), будет запущено UnhandledPromiseRejectionWarning, потому что никто не перехватывает эту ошибку, это ваша ответственность. Что касается того, что я рекомендую, зависит от того, чего вы пытаетесь достичь, если вы действительно покажете свою ready реализацию, вам будет легче дать вам лучшее решение.
Вся моя готовая функция слишком велика, но идея в том, что если я каким-то образом пропущу перехват необработанного промиса в любом обратном вызове (при готовности, при сообщении), он будет перехвачен на каком-то более высоком уровне (в блоке try catch, окружающем метод .login) . Таким образом, в таком сценарии нет никаких неожиданных выходов из процесса (просто ведение журнала). В сообщении говорится, что в будущем такие ошибки будут вызывать выходы. Теперь я вижу, что мой подход на самом деле невозможен без обертывания try-catch вокруг кода каждого обратного вызова. Спасибо за разъяснения в любом случае.
Итак, просто чтобы уточнить, вы говорите, что, поскольку само событие
on('ready')не обрабатывается с помощью.catch, отклонение обещания по существу игнорируется? Другими словами, означает ли это, что отказ от обещания, который происходит без ключевых словasync/await, не распространяется также без явногоrejectиз вызывающего кода?