← Назад к вопросам

Как задать тайм-аут для корутины

2.0 Middle🔥 181 комментариев
#Kotlin основы#Многопоточность и асинхронность

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Установка тайм-аута для корутин в Kotlin

В Kotlin Coroutines существует несколько способов установки тайм-аута для выполнения корутин. Вот основные подходы:

1. Использование функции withTimeout

Самый простой способ - использовать функцию withTimeout, которая бросает TimeoutCancellationException, если выполнение занимает больше указанного времени.

import kotlinx.coroutines.*

suspend fun fetchDataWithTimeout(): String {
    return withTimeout(3000L) { // Тайм-аут 3 секунды
        // Длительная операция
        delay(2000L) // Симуляция работы
        "Данные получены"
    }
}

fun main() = runBlocking {
    try {
        val result = fetchDataWithTimeout()
        println(result)
    } catch (e: TimeoutCancellationException) {
        println("Операция превысила тайм-аут")
    }
}

2. Использование withTimeoutOrNull

Эта функция возвращает null вместо исключения при превышении тайм-аута:

import kotlinx.coroutines.*

suspend fun fetchDataOrNull(): String? {
    return withTimeoutOrNull(2000L) { // Тайм-аут 2 секунды
        delay(3000L) // Эта операция займет 3 секунды - превысит тайм-аут
        "Успешный результат"
    }
}

fun main() = runBlocking {
    val result = fetchDataOrNull()
    println(result ?: "Тайм-аут истек, результат null")
}

3. Комбинирование с async и await

Для асинхронных операций можно комбинировать тайм-ауты с async:

import kotlinx.coroutines.*

fun main() = runBlocking {
    val deferredResult = async {
        withTimeout(1500L) {
            delay(1000L)
            "Результат вычислений"
        }
    }
    
    try {
        val result = deferredResult.await()
        println(result)
    } catch (e: TimeoutCancellationException) {
        println("Тайм-аут вычислений")
    }
}

4. Кастомная реализация с select

Для более сложных сценариев можно использовать select:

import kotlinx.coroutines.*
import kotlinx.coroutines.selects.select

suspend fun fetchWithSelect(): String {
    return select<String> {
        // Основная операция
        async { 
            delay(2500L)
            "Основные данные"
        }.onAwait { it }
        
        // Тайм-аут
        async {
            delay(2000L)
            throw TimeoutCancellationException("Тайм-аут")
        }.onAwait { throw it }
    }
}

fun main() = runBlocking {
    try {
        val result = fetchWithSelect()
        println(result)
    } catch (e: TimeoutCancellationException) {
        println("Выбрана операция тайм-аута")
    }
}

5. Глобальный тайм-аут для корутины

Можно установить тайм-аут на уровне корутины через withContext:

import kotlinx.coroutines.*

suspend fun longRunningOperation(): String = withContext(Dispatchers.IO) {
    withTimeout(5000L) {
        // Имитация длительной операции
        repeat(10) {
            delay(1000L)
            println("Шаг $it")
        }
        "Завершено"
    }
}

fun main() = runBlocking {
    try {
        val result = longRunningOperation()
        println("Результат: $result")
    } catch (e: TimeoutCancellationException) {
        println("Операция отменена по тайм-ауту")
    }
}

Важные моменты:

Отмена и ресурсы: При срабатывании тайм-аута корутина отменяется, но это кооперативная отмена. Все suspend-функции должны проверять isActive или вызывать yield() для корректной реакции на отмену.

Обработка исключений: TimeoutCancellationException является подклассом CancellationException и не обрабатывается стандартными блоками try-catch на верхнем уровне, если не указано явно.

Вложенные тайм-ауты: Можно создавать вложенные тайм-ауты, но нужно быть осторожным с обработкой исключений:

suspend fun nestedTimeouts() {
    try {
        withTimeout(5000L) {
            try {
                withTimeout(2000L) {
                    delay(3000L) // Выбросит TimeoutCancellationException
                }
            } catch (e: TimeoutCancellationException) {
                println("Внутренний тайм-аут: ${e.message}")
                // Можно продолжить выполнение внешнего блока
            }
            delay(4000L) // Это вызовет внешний тайм-аут
        }
    } catch (e: TimeoutCancellationException) {
        println("Внешний тайм-аут: ${e.message}")
    }
}

Производительность: Тайм-ауты реализованы эффективно и не создают дополнительных потоков. Они используют внутренний механизм планирования корутин.

Выбор метода зависит от конкретной задачи:

  • withTimeout - когда нужно обработать исключение
  • withTimeoutOrNull - когда важен факт успешного выполнения
  • Комбинации с async/await - для параллельных операций
  • select - для конкурентного выполнения нескольких операций с выбором первой завершенной
Как задать тайм-аут для корутины | PrepBro