Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Чому return у блоці finally небезпечний і що ще він може «проковтнути»?

Блок finally виконується після try і catch у будь-якому разі. Але якщо він сам завершується через return, throw, break чи continue, це замінює результат усього try...catch.

return у finally ковтає виняток:

function save() {
  try {
    throw new Error('Диск заповнено');
  } finally {
    return 'ok';
  }
}

save();   // 'ok' - помилка зникла без сліду

І перекриває return з try:

function getStatus() {
  try {
    return 'from try';
  } finally {
    return 'from finally';
  }
}
getStatus();   // 'from finally'

throw у finally замінює початкову помилку:

try {
  throw new Error('справжня причина');
} finally {
  cleanup();   // якщо cleanup() кине свою помилку, «справжня причина» загубиться
}

Це особливо підступно: у логах буде помилка прибирання («Cannot read properties of null»), а справжня проблема - прихована.

Що відбувається насправді. Коли try завершується (return, виняток), результат «запам'ятовується», виконується finally, і лише потім запам'ятований результат застосовується. Але якщо finally сам завершився «різко», запам'ятоване відкидається.

Ще одна тонкість - значення return обчислюється до finally:

function f() {
  let x = 1;
  try {
    return x;     // повернеться 1
  } finally {
    x = 2;        // змінна змінилася, але значення вже обчислено
  }
}

З об'єктом інакше: повертається посилання, і зміна властивостей у finally буде видна.

Правила для finally:

  • лише прибирання: закрити, зняти блокування, приховати індикатор;
  • ніколи не return, break, continue - лінтер no-unsafe-finally ловить це автоматично;
  • прибирання, що саме може впасти, загортати у власний try...catch, щоб не перекрити основну помилку:
} finally {
  try {
    await connection.close();
  } catch (closeError) {
    report(closeError);   // повідомити, але не перекрити помилку з try
  }
}

Сучасна альтернатива для гарантованого прибирання ресурсів - using з явним керуванням ресурсами (Symbol.dispose): ресурс звільняється автоматично при виході з блоку. Він уже є в TypeScript і поступово з'являється в рушіях JavaScript.

Аналогія з PHP: там return у finally теж перекриває і return, і виняток з try - правило однакове для обох мов.

Докладніше в документації: try...catch: блок finally

Схожі питання