Почему я не могу перехватить отказы обещаний discord.js в обратных вызовах событий?

Итак, я делаю дискорд-бота. Для простоты вот небольшая часть очень, которая иллюстрирует мою проблему:

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'))?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
2 259
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Причина в том, что ваш второй код не работает как эмиттер событий разногласий, как и 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') не обрабатывается с помощью .catch, отклонение обещания по существу игнорируется? Другими словами, означает ли это, что отказ от обещания, который происходит без ключевых слов async/await, не распространяется также без явного reject из вызывающего кода?

Arnav Borborah 12.05.2019 15:19

Также вы бы порекомендовали использовать второй упомянутый вами подход или просто ловить ошибки в каждом отдельном обработчике ошибок?

Arnav Borborah 12.05.2019 15:32

Я говорю, что если вы не поймаете ошибку внутри обратного вызова on('ready'), будет запущено UnhandledPromiseRejectionWarning, потому что никто не перехватывает эту ошибку, это ваша ответственность. Что касается того, что я рекомендую, зависит от того, чего вы пытаетесь достичь, если вы действительно покажете свою ready реализацию, вам будет легче дать вам лучшее решение.

Marcos Casagrande 12.05.2019 16:20

Вся моя готовая функция слишком велика, но идея в том, что если я каким-то образом пропущу перехват необработанного промиса в любом обратном вызове (при готовности, при сообщении), он будет перехвачен на каком-то более высоком уровне (в блоке try catch, окружающем метод .login) . Таким образом, в таком сценарии нет никаких неожиданных выходов из процесса (просто ведение журнала). В сообщении говорится, что в будущем такие ошибки будут вызывать выходы. Теперь я вижу, что мой подход на самом деле невозможен без обертывания try-catch вокруг кода каждого обратного вызова. Спасибо за разъяснения в любом случае.

Arnav Borborah 12.05.2019 17:08

Другие вопросы по теме