Путешествия

Отладка кода в JavaScript: как выявить причину ошибки с помощью Error.cause

Краткое резюме

Свойство cause в JavaScript помогает упростить отладку кода, сохраняя исходную ошибку и её стек вызовов при обработке исключений. Это позволяет более эффективно выявлять первопричины ошибок в сложных структурах кода.

Обработка ошибок в JavaScript зачастую была сопряжена с определёнными сложностями. Обнаружить ошибку несложно, однако выявить её первопричину может оказаться весьма трудоёмкой задачей. В таких случаях на помощь приходит свойство cause. **Трудности традиционной обработки ошибок** В сложных многоуровневых структурах кода, таких как сервисы, вызывающие другие сервисы, или функции-обёртки, легко потерять след того, что именно вызвало проблему. Обычно код в таких ситуациях выглядит следующим образом: ``` try { JSON.parse('{ bad json }'); } catch (err) { throw new Error('Something went wrong: ' + err.message); } ``` Хотя ошибка и была обработана, исходный стек вызовов и тип ошибки при этом теряются. **Новый подход с Error.cause** Свойство cause позволяет сохранить исходную ошибку без потерь: ``` try { try { JSON.parse('{ bad json }'); } catch (err) { throw new Error('Something went wrong', { cause: err }); } } catch (err) { console.error(err.stack); console.error('Caused by:', err.cause.stack); } ``` При использовании Error.cause становятся доступны оба стека вызовов: ``` Error: Something went wrong at ... Caused by: SyntaxError: Unexpected token b in JSON at position 2 at JSON.parse () at ... ``` Теперь исходная ошибка сохраняется, а на верхнем уровне отображается понятное сообщение. **Практическое применение** ``` function fetchUserData() { try { JSON.parse('{ broken: true }'); // ← Здесь возникнет ошибка } catch (parseError) { throw new Error('Failed to fetch user data', { cause: parseError }); } } try { fetchUserData(); } catch (err) { console.error(err.message); // "Failed to fetch user data" console.error(err.cause); // [SyntaxError: Unexpected token b in JSON] console.error(err.cause instanceof SyntaxError); // true } ``` Это решение весьма удобно в использовании. Согласно спецификации, свойство cause не является перечисляемым при передаче через конструктор Error, поэтому оно не попадает в логи и циклы for...in, если не обращаться к нему явно. Это также относится к свойствам message и stack. Важно отметить, что JavaScript не объединяет стеки вызовов автоматически. Чтобы получить полный стек вызовов, необходимо вручную обратиться к err.cause.stack. **Альтернативные решения до появления cause** До введения cause в ES2022 разработчики прибегали к различным обходным путям, таким как конкатенация строк, использование собственных свойств вроде .originalError или полное оборачивание ошибки. Эти методы часто приводили к потере важных данных, таких как исходный стек вызовов или тип ошибки. Свойство cause решает эту проблему стандартным и чистым способом. **Применение с пользовательскими ошибками** Cause можно использовать и в собственных классах ошибок: ``` class DatabaseError extends Error { constructor(message, { cause } = {}) { super(message, { cause }); this.name = 'DatabaseError'; } } ``` Если используется среда выполнения ES2022 и новее, этого достаточно — super(message, { cause }) обработает всё автоматически.

Фильтры и сортировка