Вопрос или проблема
Предположим, что есть асинхронная функция с ошибкой в реализации, например:
function resolveAfter2Seconds() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(undefined_variable);
}, 2000);
});
}
Как я могу вызвать эту функцию и обработать ReferenceError
, который она вызовет?
Я пытался использовать блок .catch
следующим образом:
resolveAfter2Seconds().then(result => console.log(result)).catch(error => console.log(error));
А также оберточную функцию вот так:
async function safeAsyncCall() {
try {
return await resolveAfter2Seconds()
} catch {
console.log("Что-то пошло не так")
}
}
safeAsyncCall().then(result => console.log(result))
Ни один из этих вариантов не работает (код все равно завершает свою работу с ReferenceError
).
Как я могу безопасно вызвать эту функцию?
Я понимаю, что это не то, что вы хотите слышать, но я не думаю, что существует безопасный способ вызвать эту функцию. Это просто сломанный код.
Вы можете обойти это, как предложил KLASANGUI в своем ответе, но я бы предпочёл это исправить.
Если эта библиотека установлена через npm, т.е. в вашем node_modules, я бы изменил её из папки node_modules и использовал patch-package или yarn patch. Это создаст файл патча из ваших изменений, который затем можно будет применять каждый раз, когда выполняется npm install
, используя что-то вроде postinstall.
Как человек, который сталкивался с множеством сломанных пакетов js, я бы поступил именно так. Мне не нравится делать странные хак-решения в своем коде из-за сломанного кода других.
Ответ на вопрос Оригинального автора зависит от среды, в которой будет выполняться код, и от того, является ли скрипт ESModule или CommonJS.
В браузерах, например, вы можете просто слушать необработанные ошибки вот так:
window.addEventListener
(
'error', error =>
{
// Обработка необработанных ошибок здесь...
}
);
Окно: событие ошибки –
Событие ошибки возникает у объекта Window, когда ресурс не удалось загрузить или использовать — например, если у скрипта есть ошибка выполнения.
С https://developer.mozilla.org/en-US/docs/Web/API/Window/error_event
Обратите внимание, что в вашем коде, если он выполняется в браузере, будет генерироваться ошибка в ESModule режиме, потому что в CommonJS режиме он пытается получить переменную как свойство window
, что должно вернуть undefined
(в большинстве случаев).
В NodeJs вы можете сделать это так:
process.on
(
'uncaughtException', error =>
{
// Обработка необработанных ошибок здесь...
}
})
Каждая среда должна иметь свой способ сделать это…
ИЗМЕНЕНИЕ:
Пример для конкретной среды ОА:
const eventEmmiter = new ( await import('events') ).EventEmitter;
app.get('/some/route', ( request, response ) =>
{
// Передать ответ другому обработчику ошибок
// доступному из всех маршрутов.
const errorHandler = error => trueErrorHandler(error, request, response, errorHandler);
eventEmmiter.on('error', errorHandler);
// Здесь выполняйте свои действия маршрута...
});
const trueErrorHandler = ( error, request, response, errorHandler ) =>
{
// Обработка ваших ошибок из всех маршрутов здесь?
// Прежде чем возвращать...
eventEmmiter.removeListener('error', errorHandler);
}
Вы можете просто повторить шаблон на каждом маршруте… Или вы можете управлять исключениями конкретно в каждом маршруте, вместо того чтобы использовать trueErrorHandler()
.
Лучший вариант — не использовать код библиотеки — подайте отчет об ошибке и напишите свою функцию задержки на 2 секунды.
Есть ужасный хак, который вы можете попробовать, несмотря на то, как ужасно вы можете о нем думать: определите переменную, используя имя, сообщенное в синтаксических ошибках, о которых вы знаете. Это может сработать, потому что если она не определена, никто другой не определял её.
-
Если вы хотите ловить ошибки промисов каждый раз, когда используется функция задержки, определите её как отклоненный промис:
const rejectPromise = Promise.reject("resolveAfter2Seconds не работает - не вызывайте"); rejectPromise.catch(()=>null); global[undefined_variable_name_string] = rejectPromise;
Обратите внимание, что вам нужно будет предоставить обработчик
catch
при вызовахresolveAfter2Seconds
— он не сгенерирует необработанное отклонение, если вы забудете. -
Если вы хотите, чтобы
resolveAfter2Seconds
действительно работал, вы можете определить переменную какundefined
:global[undefined_variable_name_string] = undefined;
Предупреждение Это самый худший вид хаков, о котором я когда-либо задумывался. Я надеюсь, что это не будет использовано..
Почему это может быть необходимо
Синтаксическая ошибка возникает в функции обратного вызова таймера при подготовке аргумента, который необходимо передать в resolve
(который никогда не вызывается). Поскольку функции обратного вызова таймера вызываются с чистым стеком, их не наблюдает код Promise как задачу Promise в очереди микро-задач, и ошибка никогда не появится в обработчике catch. Точнее, промис, возвращаемый resolveAfter2Seconds
, просто остается в состоянии ожидания.
Ответ или решение
Как обработать неожиданные ошибки, возникающие внутри асинхронной функции в JavaScript
В современном программировании работа с асинхронным кодом становится неотъемлемой частью процесса разработки. Однако, как показывает практика, асинхронные функции могут порой вызывать неожиданные ошибки, которые могут повлечь за собой дескриптивные сбои приложения. Рассмотрим, как можно правильно обрабатывать такие ошибки на примере неправильно реализованной асинхронной функции.
Пример: Ошибка в асинхронной функции
Допустим, у вас есть следующая функция, которая вызывает ошибку ReferenceError
из-за неопределенной переменной:
function resolveAfter2Seconds() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(undefined_variable);
}, 2000);
});
}
Если мы попробуем вызвать эту функцию, используя стандартные методы обработки ошибок, такие как .catch()
или try-catch
в асинхронной функции, то получим неожиданные результаты.
Основные способы обработки ошибок
-
Использование обработки ошибок на уровне промисов:
Можно вызывать функцию и обрабатывать ошибки в цепочке промисов. Например:
resolveAfter2Seconds() .then(result => console.log(result)) .catch(error => console.log('Ошибка:', error));
Однако, в этом случае
ReferenceError
может не попасть в.catch()
, так как ошибка происходит не в промисе напрямую, а в его внутреннем обработчике. -
Использование
try-catch
с асинхронной функцией:Второй подход – это использование
try-catch
внутри асинхронной функции:async function safeAsyncCall() { try { return await resolveAfter2Seconds(); } catch (error) { console.log("Что-то пошло не так:", error); } } safeAsyncCall().then(result => console.log(result));
Однако данный метод также не будет работать, если ошибка возникает в контексте таймера.
Глобальная обработка ошибок
Для того чтобы обеспечить профилактику неожиданных сбоев, важно применять глобальные обработчики ошибок, как для браузеров, так и для Node.js.
-
В браузерах можно использовать событие
error
:window.addEventListener('error', function(event) { console.log('Ловим глобальную ошибку:', event.message); });
-
В Node.js можно установить обработчик на событие
uncaughtException
:process.on('uncaughtException', (error) => { console.log('Ловим необработанную ошибку:', error); });
Эти методы позволяют отслеживать и обрабатывать ошибки, которые не были пойманы в обработчиках.
Заключение
Ошибки, возникающие в асинхронных функциях, могут существенно повлиять на стабильность работы вашего приложения. Используя комплексный подход к обработке ошибок, включая локальные и глобальные обработчики, можно значительно улучшить устойчивость кода.
Тем не менее, рекомендую обращаться к качеству кода и по возможности избегать использования «хаков» для обхода ошибок в зависимостях или стороннем коде. Прежде всего, стоит исправить ошибку в самой функции, чтобы избежать подобных ситуаций в будущем. Кроме того, всегда полезно оставлять комментарии или документацию вашей логике обработки ошибок, чтобы ваша команда могла быстро интегрироваться в проект и понимать, как именно обрабатываются потенциальные неисправности.