Пошук уроків, статей та іншого контенту
Розберете блок finally та навчитеся виконувати очищення ресурсів незалежно від того, чи виникла помилка.
finallyПід час виконання коду можуть виникати помилки. Для їх обробки JavaScript використовує конструкцію try...catch:
try — код, у якому може виникнути помилка;
catch — обробник помилки;
finally — код, який виконається після завершення try або catch.
Блок finally зручно використовувати для очищення ресурсів:
закриття з'єднання;
зупинки індикатора завантаження;
видалення тимчасових даних;
розблокування інтерфейсу;
звільнення зайнятого ресурсу.
try {
// Код, у якому може виникнути помилка
} catch (error) {
// Обробка помилки
} finally {
// Код, який виконається завжди
}Блок catch є необов'язковим, але без нього try повинен мати finally:
try {
console.log("Виконуємо операцію");
} finally {
console.log("Очищення після операції");
}finallytry {
console.log("Операція успішна");
} catch (error) {
console.log("Виникла помилка");
} finally {
console.log("Очищення");
}Результат:
Операція успішна
ОчищенняОскільки помилки не було, блок catch пропускається. Блок finally усе одно виконується.
try {
throw new Error("Не вдалося виконати операцію");
} catch (error) {
console.log("Помилка:", error.message);
} finally {
console.log("Очищення");
}Результат:
Помилка: Не вдалося виконати операцію
ОчищенняСпочатку виконується catch, а потім — finally.
Уявімо функцію, яка починає роботу з певним ресурсом. Ресурс потрібно закрити незалежно від результату операції.
function processData(shouldFail) {
let resourceIsOpen = false;
try {
resourceIsOpen = true;
console.log("Ресурс відкрито");
if (shouldFail) {
throw new Error("Помилка під час обробки даних");
}
console.log("Дані успішно оброблено");
} catch (error) {
console.log("Не вдалося завершити операцію:", error.message);
} finally {
if (resourceIsOpen) {
resourceIsOpen = false;
console.log("Ресурс закрито");
}
}
}
processData(false);
console.log("---");
processData(true);Результат:
Ресурс відкрито
Дані успішно оброблено
Ресурс закрито
---
Ресурс відкрито
Не вдалося завершити операцію: Помилка під час обробки даних
Ресурс закритоУ другому виклику сталася помилка, але очищення все одно відбулося.
finally із returnБлок finally виконується навіть тоді, коли в try або catch є return.
function getMessage() {
try {
return "Результат із try";
} finally {
console.log("finally виконався");
}
}
console.log(getMessage());Результат:
finally виконався
Результат із tryСпочатку JavaScript готує повернення значення, виконує finally, а потім завершує функцію.
return у finally без потребиreturn у finally може замінити результат, який повертається з try або catch.
function getMessage() {
try {
return "Результат із try";
} finally {
return "Результат із finally";
}
}
console.log(getMessage());Результат:
Результат із finallyЩе небезпечніша ситуація виникає, якщо помилка була викинута в try:
function run() {
try {
throw new Error("Важлива помилка");
} finally {
return "Помилку приховано";
}
}
console.log(run());У цьому випадку помилка не буде передана далі, бо return із finally перериває її поширення.
Зазвичай у finally потрібно виконувати лише очищення, а не повертати значення.
finally з асинхронними операціямиfinally також можна використовувати разом із async та await. Для промісів існує метод .finally(), який виконується після успішного або невдалого завершення проміса.
function delay(milliseconds) {
return new Promise((resolve) => {
setTimeout(resolve, milliseconds);
});
}
async function loadData() {
console.log("Показуємо індикатор завантаження");
try {
await delay(500);
console.log("Дані завантажено");
} catch (error) {
console.log("Помилка завантаження:", error.message);
} finally {
console.log("Приховуємо індикатор завантаження");
}
}
loadData();Навіть якщо під час завантаження виникне помилка, індикатор можна приховати в finally.
Для промісів можна записати аналогічну логіку так:
function loadData() {
console.log("Показуємо індикатор завантаження");
return Promise.resolve("Дані")
.then((data) => {
console.log("Отримано:", data);
})
.catch((error) => {
console.log("Помилка:", error.message);
})
.finally(() => {
console.log("Приховуємо індикатор завантаження");
});
}
loadData();Метод .finally() не отримує результат проміса або об'єкт помилки як аргумент. Він призначений для дій, які не залежать від результату операції.
finally замість обробки помилкиfinally не призначений для аналізу помилки:
try {
// Операція
} finally {
// Це не обробка помилки
}Якщо потрібно відреагувати на помилку, використовуйте catch:
try {
// Операція
} catch (error) {
console.log("Помилка:", error.message);
} finally {
console.log("Очищення");
}finally виконається абсолютно завждиfinally виконується під час звичайного завершення конструкції try...catch. Однак він не врятує програму від ситуацій, коли середовище примусово припинило виконання, наприклад через аварійне завершення процесу або вимкнення пристрою.
Тому слово «завжди» означає: після звичайного успішного завершення, помилки або return у цій конструкції.
Без finally код очищення часто дублюють:
try {
// Операція
// Очищення після успіху
} catch (error) {
// Обробка помилки
// Очищення після помилки
}Краще винести спільне очищення в одне місце:
try {
// Операція
} catch (error) {
// Обробка помилки
} finally {
// Спільне очищення
}return у finallyНе повертайте значення з finally, якщо для цього немає особливої причини. Такий return може:
змінити результат функції;
приховати помилку;
ускладнити налагодження програми.
finally виконується після try або catch.
Він підходить для очищення ресурсів і завершальних дій.
Блок finally виконується і за успішного виконання, і після помилки.
finally виконується навіть перед завершенням функції через return.
Не варто використовувати return у finally, адже він може замінити результат або приховати помилку.
Для асинхронного коду можна використовувати try...catch...finally з async/await або метод .finally() у промісів.