← Назад к вопросам
Как отменить выполнение корутины
2.2 Middle🔥 221 комментариев
#Kotlin основы#Многопоточность и асинхронность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Отмена выполнения корутины
Отмена (cancellation) - это встроенный механизм Kotlin корутин для контролируемого завершения.
Основные способы отмены
1. Через Job.cancel()
val job = launch {
try {
delay(5000)
println("Done")
} catch (e: CancellationException) {
println("Cancelled")
throw e // Переброс обязателен!
}
}
// Отменить корутину
job.cancel()
2. Через cancelAndJoin()
val job = launch {
repeat(10) { i ->
println("Working $i")
delay(1000)
}
}
// Отменить и ждать завершения
job.cancelAndJoin()
3. Через CoroutineScope.cancel()
val scope = CoroutineScope(Dispatchers.Main + Job())
scope.launch {
delay(5000)
println("Done")
}
// Отменить ВСЕ корутины в scope
scope.cancel()
4. ViewModel scope (автоматическая отмена)
class MyViewModel : ViewModel() {
fun loadData() {
viewModelScope.launch {
val data = fetchData() // Отменится при очистке ViewModel
_uiState.value = data
}
}
// При onCleared() автоматически отменяются все корутины
}
5. Lifecycle scope (автоматическая отмена)
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
// Отменится при DESTROY activity
val data = fetchData()
updateUI(data)
}
lifecycleScope.launchWhenStarted {
// Приостановится при PAUSE, возобновится при RESUME
observeData()
}
}
}
6. Timeout отмена
launchIO {
try {
withTimeoutOrNull(5000L) {
heavyComputation() // Отменится через 5 секунд
}
} catch (e: TimeoutCancellationException) {
println("Timed out")
}
}
Внутренний механизм отмены
Как работает cancel():
- Устанавливается флаг отмены в Job
- При следующей suspend точке (delay, network call и т.д.) выбрасывается CancellationException
- Корутина завершается
- Finally блоки выполняются
val job = launch {
try {
println("1. Start")
delay(1000) // Здесь выбросится CancellationException
println("2. Never reached")
} finally {
println("3. Cleanup (выполнится!)")
}
}
delay(100)
job.cancel() // Отменить
// Output:
// 1. Start
// 3. Cleanup
Проверка отмены в цикле
launch {
repeat(1000) { i ->
println("Item $i")
delay(100)
// Или явная проверка
ensureActive() // Выбросит CancellationException если отменена
}
}
// Альтернатива
launch {
repeat(1000) { i ->
if (!isActive) return@launch // Проверить статус
println("Item $i")
delay(100)
}
}
Graceful shutdown
class DataRepository {
private val scope = CoroutineScope(Dispatchers.IO + Job())
fun loadData() {
scope.launch {
try {
while (isActive) {
val data = fetchData()
updateCache(data)
delay(5000)
}
} finally {
// Очистить ресурсы
closeConnections()
saveCacheToDb()
}
}
}
fun cleanup() {
scope.cancel() // Отменить все корутины
}
}
Обработка CancellationException
launch {
try {
delay(5000)
} catch (e: CancellationException) {
// Обработать отмену
println("Cancelled")
throw e // ВАЖНО: переброс!
} catch (e: Exception) {
// Обработать другие ошибки
println("Error: $e")
}
}
Отмена с причиной
val job = launch {
delay(5000)
}
job.cancel(CancellationException("User cancelled"))
// Или с явным сообщением
job.cancel("Timeout")
Проверка состояния Job
val job = launch {
delay(5000)
}
println("Active: ${job.isActive}") // true
job.cancel()
println("Cancelled: ${job.isCancelled}") // true
println("Active: ${job.isActive}") // false
// Ждать завершения
job.join()
println("Completed: ${job.isCompleted}") // true
Супервизор (обработка ошибок в корутинах)
// SupervisorJob не отменяет другие корутины при ошибке
val supervisor = SupervisorJob()
val scope = CoroutineScope(Dispatchers.Main + supervisor)
scope.launch {
// Ошибка здесь НЕ повлияет на другие корутины
throw Exception("Error in task 1")
}
scope.launch {
// Продолжит работать
delay(5000)
println("Task 2 completed")
}
Лучшие практики
✅ Делай так:
- Используй viewModelScope для Activity/Fragment
- Используй lifecycleScope для привязки к lifecycle
- Всегда переброс CancellationException в catch
- Проверяй isActive в циклах
- Используй withTimeout для длительных операций
❌ Избегай:
- Игнорирования CancellationException
- Ловли CancellationException без переброса
- Забывания про finally блоки
- Создания неуправляемых CoroutineScope
- Игнорирования отмены в loops
Вывод
Отмена корутин - это встроенный механизм, поддерживаемый на уровне suspend функций. Используй Job.cancel() или lifecycle-based scope для автоматической отмены при очистке.