← Назад к вопросам
Как выполнить операцию вне зависимости от итога выполнения Promise?
2.3 Middle🔥 241 комментариев
#JavaScript Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Promise.finally(): выполнение кода независимо от итога
Для выполнения операции независимо от того, успешно ли выполнился Promise (resolve или reject), используется метод .finally(). Это гарантирует, что код в блоке finally выполнится в любом случае.
Синтаксис
promise
.then(result => console.log('Success:', result))
.catch(error => console.error('Error:', error))
.finally(() => console.log('Done, regardless of outcome'));
Основной пример
const fetchUser = (id) => {
return fetch(`/api/users/${id}`)
.then(response => response.json())
.then(data => {
console.log('User:', data);
return data;
})
.catch(error => {
console.error('Failed to fetch:', error);
throw error;
})
.finally(() => {
console.log('Fetch operation completed');
// Здесь выполнится ВСЕГДА — и при успехе, и при ошибке
});
};
fetchUser(1)
.then(user => console.log('Got user:', user))
.catch(err => console.log('Handled error:', err));
Вывод при успехе:
User: {id: 1, name: 'John'}
Fetch operation completed
Got user: {id: 1, name: 'John'}
Вывод при ошибке:
Failed to fetch: Error: Network error
Fetch operation completed
Handled error: Error: Network error
Типичные варианты использования
1. Остановка loading индикатора
export function useApi<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const fetchData = () => {
setLoading(true);
setError(null);
fetch(url)
.then(r => r.json())
.then(data => setData(data))
.catch(err => setError(err))
.finally(() => setLoading(false)); // Всегда останавливает loading
};
return { data, loading, error, fetchData };
}
2. Очистка ресурсов
let connection;
const openConnection = (url) => {
return new Promise((resolve, reject) => {
connection = new WebSocket(url);
connection.onopen = () => resolve('Connected');
connection.onerror = () => reject(new Error('Connection failed'));
})
.finally(() => {
// Очистка ресурсов — выполнится независимо от результата
console.log('Connection process finished, cleanup resources');
});
};
3. Скрытие модального диалога
const submitForm = (data) => {
setSubmitting(true);
return api.post('/form', data)
.then(response => {
showSuccess('Form submitted!');
return response.data;
})
.catch(error => {
showError(error.message);
throw error;
})
.finally(() => {
// Закрыть модальное окно в любом случае
closeModal();
setSubmitting(false);
});
};
4. Логирование
const executeTask = (task) => {
const startTime = Date.now();
return performTask(task)
.finally(() => {
const duration = Date.now() - startTime;
console.log(`Task "${task.name}" took ${duration}ms`);
// Логирование выполнится всегда
});
};
async/await с finally
// С finally блоком
async function fetchAndProcess() {
let data;
try {
const response = await fetch('/api/data');
data = await response.json();
console.log('Data:', data);
} catch (error) {
console.error('Error:', error);
} finally {
console.log('Operation complete');
}
return data;
}
Это эквивалентно:
function fetchAndProcess() {
return fetch('/api/data')
.then(response => response.json())
.then(data => {
console.log('Data:', data);
return data;
})
.catch(error => {
console.error('Error:', error);
})
.finally(() => {
console.log('Operation complete');
});
}
Важные детали
1. finally НЕ изменяет результат Promise
Promise.resolve(5)
.finally(() => 'Ignored value')
.then(value => console.log(value)); // 5, не 'Ignored value'
Promise.reject(new Error('Oops'))
.finally(() => 'Ignored value')
.catch(error => console.log(error)); // Error: Oops
2. Если finally бросит ошибку, она подавит предыдущую
Promise.reject(new Error('First error'))
.finally(() => {
throw new Error('Second error');
})
.catch(error => {
console.log(error.message); // 'Second error' — первая ошибка подавлена
});
3. Порядок выполнения
console.log('Start');
Promise.resolve('value')
.then(v => { console.log('Then:', v); return v; })
.finally(() => { console.log('Finally'); })
.then(() => { console.log('Then after finally'); });
console.log('End');
Вывод:
Start
End
Then: value
Finally
Then after finally
React компонент с finally
export function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
let mounted = true;
setLoading(true);
setError(null);
fetch(`/api/users/${userId}`)
.then(r => {
if (!r.ok) throw new Error('Failed to fetch');
return r.json();
})
.then(data => {
if (mounted) setUser(data);
})
.catch(err => {
if (mounted) setError(err);
})
.finally(() => {
if (mounted) setLoading(false);
});
return () => {
mounted = false; // Cleanup при unmount
};
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
if (!user) return null;
return <div>{user.name}</div>;
}
Сравнение подходов
// ❌ Без finally — нужно дублировать cleanup
.then(data => {
setLoading(false);
return data;
})
.catch(error => {
setLoading(false);
throw error;
})
// ✅ С finally — cleanup один раз
.finally(() => {
setLoading(false);
})
finally для отладки
const debugPromise = (promise, name) => {
return promise
.then(result => {
console.log(`[${name}] Resolved:`, result);
return result;
})
.catch(error => {
console.error(`[${name}] Rejected:`, error);
throw error;
})
.finally(() => {
console.log(`[${name}] Finished`);
});
};
// Использование
debugPromise(fetch('/api/data'), 'API Call')
.then(r => r.json())
.then(data => console.log('Success:', data))
.catch(err => console.log('Handled:', err));
Итог
.finally() используется для:
- Остановки loading индикаторов
- Закрытия модальных окон
- Очистки ресурсов (соединения, таймеры)
- Логирования операций
- Убирания дублирования cleanup кода
Это essential паттерн для надёжной обработки асинхронного кода в JavaScript.