Кратко
СкопированоКонструкция try позволяет выполнить произвольный код, но если в нем произошла ошибка, то программа не остановит своё выполнение, а перейдёт в блок catch, где ошибку можно обработать.
Как понять
СкопированоОшибки в программах это очень неприятно, но никто не застрахован от их появления. К тому же, ошибки могут появляться в тех ситуациях, которые не зависят от нас самих. Например, пользователь неправильно воспользовался программой. Поэтому в языке необходимы конструкции, которые позволяют выполнить произвольный код, но в случае ошибки дать возможность её обработать.
В JavaScript ситуация ещё сложнее. Если во время работы скрипта возникла ошибка и она не была обработана, то выполнение останавливается, и программа больше не работает.
Конструкция try делает программы стабильнее — в случае ошибки мы можем продолжить выполнение. Мы можем написать любой синтаксически верный код и, если он выполнится без ошибок, то отлично. Если же что-то пойдёт не так, то выполнится код, написанный в catch.
Представьте, что вы тренируетесь отбивать мяч в бейсбол. У вас есть партнёр, который будет ловить мяч, в случае вашей ошибки, если отбить мяч не удастся. В большинстве попыток у вас все будет получаться хорошо, но если же случится промах, то второй игрок поймает мяч и вернёт его на место, чтобы можно было попробовать снова.
С пойманной в catch ошибкой можно поступить как угодно: отправить данные в специальный логгер, обработать данные из неё и даже выбросить новую ошибку, которая может быть поймана в другом месте и т.д.
Как пишется
СкопированоБазовый случай
СкопированоЧтобы использовать try, необходимо в блоке try написать код, который нужно исполнить, а в блоке catch написать, что делать в случае ошибки.
try { someFunction() anotherFunction()} catch (err) { console.log('Поймали ошибку! Вот она: ', err.message)}
try {
someFunction()
anotherFunction()
} catch (err) {
console.log('Поймали ошибку! Вот она: ', err.message)
}
Если в блоке try не произошло ошибок, то код в блоке catch не выполнится.
Важно помнить, что код в try должен быть синтаксически верным. Если написать невалидный код (например, не закрыть фигурные скобки), то скрипт не запустится, потому что JavaScript не поймёт код. Ошибки, которые обработает блок catch, будут ошибками во время выполнения программы.
В случае ошибки выполнение в блоке try прерывается и сразу же переходит в блок catch . После него скрипт продолжит своё выполнение, как и прежде.
try { const six = 6 // 1. Создаём константу console.log(six) // 2. Выведет 6 six = 7 // Ошибка! Присваиваем новое значение в const // с этого места управление переходит в catch const nine = 9 // не выполнится console.log(six + nine) // и это тоже не исполнится} catch (err) { console.log('Поймали ошибку!') // 3. Обработали ошибку}console.log('Что ж, можно и продолжать') // 4. Будет выполняться дальше
try {
const six = 6 // 1. Создаём константу
console.log(six) // 2. Выведет 6
six = 7 // Ошибка! Присваиваем новое значение в const
// с этого места управление переходит в catch
const nine = 9 // не выполнится
console.log(six + nine) // и это тоже не исполнится
} catch (err) {
console.log('Поймали ошибку!') // 3. Обработали ошибку
}
console.log('Что ж, можно и продолжать') // 4. Будет выполняться дальше

finally
СкопированоРассмотрим ситуацию, когда в случае успеха или неудачи выполнения какого-то участка кода нам необходимо проводить какие-то действия, чтобы корректно завершить работу скрипта. Например, нужно закрыть WebSocket-соединение после работы с ним.
let sockettry { // Создаём сокет, но в конце нужно обязательно закрыть соединение socket = new WebSocket('wss://example.com/ws') callMayThrowError()} catch (err) { ...}// Пробуем закрыть соединение после try...catchsocket?.close()
let socket
try {
// Создаём сокет, но в конце нужно обязательно закрыть соединение
socket = new WebSocket('wss://example.com/ws')
callMayThrowError()
} catch (err) {
...
}
// Пробуем закрыть соединение после try...catch
socket?.close()
Казалось бы никаких проблем с этим кодом быть не должно: неважно, выполнится код в блоке try или попадёт в catch без повторного throw, следующая строка должна выполниться. Однако возможна ситуация, когда в блоке catch тоже возникнет ошибка, и тогда выполнение следующей строки уже не случится.
function doSomeWithError(e) { throw new Error('new error')}let sockettry { // Создаём сокет, но в конце нужно обязательно закрыть соединение socket = new WebSocket('wss://example.com/ws') callMayThrowError()} catch (err) { // Здесь тоже может возникнуть ошибка doSomeWithError(err)}// В случае ошибки эта строка уже не выполнитсяsocket?.close()
function doSomeWithError(e) {
throw new Error('new error')
}
let socket
try {
// Создаём сокет, но в конце нужно обязательно закрыть соединение
socket = new WebSocket('wss://example.com/ws')
callMayThrowError()
} catch (err) {
// Здесь тоже может возникнуть ошибка
doSomeWithError(err)
}
// В случае ошибки эта строка уже не выполнится
socket?.close()
Как же тогда гарантированно закрыть соединение при любом исходе?
В конструкцию try можно добавить блок finally, который выполнится после блоков try и catch. Неважно какой код выполнился в предыдущих блоках, после их завершения (даже если из catch была выброшена новая ошибка) исполнится код в блоке finally.
let sockettry { socket = new WebSocket('wss://example.com/ws') callMayThrowError()} catch (err) { // Здесь тоже может возникнуть ошибка doSomeWithError(err)} finally { // Выполнится всегда, даже если catch выбросил новую ошибку socket?.close()}
let socket
try {
socket = new WebSocket('wss://example.com/ws')
callMayThrowError()
} catch (err) {
// Здесь тоже может возникнуть ошибка
doSomeWithError(err)
} finally {
// Выполнится всегда, даже если catch выбросил новую ошибку
socket?.close()
}

Наличие блока finally необязательно. finally можно использовать и без блока catch.
try { // Отправить данные на сервер, здесь нам неважна обработка ошибки sendData()} finally { // Закрыть соединение при любом результате closeConnection()}
try {
// Отправить данные на сервер, здесь нам неважна обработка ошибки
sendData()
} finally {
// Закрыть соединение при любом результате
closeConnection()
}
Ошибки в catch
СкопированоМожет возникнуть ситуация, когда скрипт обработки ошибки тоже может сломаться. При этом могут возникнуть случаи, когда мы хотим намеренно выбросить новую ошибку из catch, или пробросить текущую, чтобы её обработать в другом месте.
Хорошей практикой считается обрабатывать в модуле только те ошибки, которые связаны непосредственно с ним, а все остальные пробрасывать дальше.
// parse-module.js// Есть свой тип ошибкиclass ParsingError extends Error { ...}function parse(data) { try { parseData(data) } catch (err) { if (err.name !== 'ParsingError') { // Другой тип ошибок пробрасываем дальше throw err } logError(err)}}
// parse-module.js
// Есть свой тип ошибки
class ParsingError extends Error {
...
}
function parse(data) {
try {
parseData(data)
} catch (err) {
if (err.name !== 'ParsingError') {
// Другой тип ошибок пробрасываем дальше
throw err
}
logError(err)
}
}
Таким образом, можно разделить ответственность, а обработкой проброшенной ошибки займётся внешний catch.
import parse from 'parse-module'try { parse(data)} catch (e) { console.log('Неизвестная ошибка парсинга:', e)}
import parse from 'parse-module'
try {
parse(data)
} catch (e) {
console.log('Неизвестная ошибка парсинга:', e)
}
Ошибки в асинхронном коде
СкопированоКонструкция try работает только синхронно. Таким образом, с помощью try нельзя обработать ошибку, которая возникла в асинхронном коде.
try { // Код выполнится корректно, т.к. отсюда вернулся промис Promise.reject('err')} catch (e) { // Ошибка не будет поймана console.log('Ошибка', e)}try { // Здесь также код выполнится корректно, потому что установил таймаут без ошибок setTimeout(() => { throw Error('ошибка') }, 1000)} catch (e) { // Ошибка из таймаута также сюда не попадёт console.log('Ошибка', e)}
try {
// Код выполнится корректно, т.к. отсюда вернулся промис
Promise.reject('err')
} catch (e) {
// Ошибка не будет поймана
console.log('Ошибка', e)
}
try {
// Здесь также код выполнится корректно, потому что установил таймаут без ошибок
setTimeout(() => {
throw Error('ошибка')
}, 1000)
} catch (e) {
// Ошибка из таймаута также сюда не попадёт
console.log('Ошибка', e)
}
Однако, если записать асинхронный код в синхронном стиле с помощью async/await, то в этом случае обработку ошибок можно осуществлять с помощью try.
async function handlePromise() { try { // Промис вернется с ошибкой await Promise.reject('err') } catch (e) { // Теперь ошибка будет поймана console.log('Ошибка', e) // err }}handlePromise()
async function handlePromise() {
try {
// Промис вернется с ошибкой
await Promise.reject('err')
} catch (e) {
// Теперь ошибка будет поймана
console.log('Ошибка', e) // err
}
}
handlePromise()
Чтобы поймать ошибку из set, блоки try должны находиться внутри функции.
На практике
Скопированосоветует
СкопированоЛюбой асинхронный код можно переписать в синхронном стиле через async, чтобы использовать единый стиль обработки ошибок, используя try. Например, перепишем установку таймаута из примера выше:
function wait(ms) { return new Promise((resolve) => setTimeout(resolve, ms))}async function timeout(fn, ms) { try { // Ждем таймаут await wait(ms) // И выполняем функцию fn() } catch (err) { // Ловим ошибку console.log('Ошибка', err) }}
function wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
async function timeout(fn, ms) {
try {
// Ждем таймаут
await wait(ms)
// И выполняем функцию
fn()
} catch (err) {
// Ловим ошибку
console.log('Ошибка', err)
}
}
Теперь можно вызывать функцию как прежде, ошибка будет поймана.
timeout(() => { throw Error('ошибка')}, 1000)
timeout(() => {
throw Error('ошибка')
}, 1000)