Как задать тайм-аут для корутины
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Установка тайм-аута для корутин в 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- для конкурентного выполнения нескольких операций с выбором первой завершенной