withTimeout и withTimeoutOrNull
Ограничивайте время выполнения с помощью обёрток тайм-аута и обрабатывайте TimeoutCancellationException.
«withTimeout и withTimeoutOrNull» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.
Зачем нужны тайм-ауты
Корутины могут зависнуть на неопределённое время при работе с сетью или вводом-выводом. withTimeout отменяет блок, если он не завершается за указанное количество миллисекунд.
import kotlinx.coroutines.*
fun main() = runBlocking {
try {
withTimeout(200) {
delay(1000) // simulates slow network
println("This never prints")
}
} catch (e: TimeoutCancellationException) {
println("Timed out!")
}
}Основы withTimeout
withTimeout(millis) { ... } выбрасывает TimeoutCancellationException (подкласс CancellationException), если выполнение блока превышает установленный предел.
import kotlinx.coroutines.*
suspend fun fetchData(): String {
delay(100)
return "data"
}
fun main() = runBlocking {
val result = withTimeout(500) {
fetchData()
}
println(result) // data
}withTimeoutOrNull
withTimeoutOrNull возвращает null при превышении тайм-аута вместо выбрасывания исключения, поэтому тайм-ауты проще обрабатывать как обычный результат.
import kotlinx.coroutines.*
fun main() = runBlocking {
val result: String? = withTimeoutOrNull(200) {
delay(1000)
"done"
}
println(result ?: "Timeout — using default")
}Тайм-аут с возвращаемым значением
При успешном выполнении обе функции возвращают значение последнего выражения в блоке.
import kotlinx.coroutines.*
fun main() = runBlocking {
val response = withTimeoutOrNull(500) {
delay(100)
mapOf("status" to 200, "body" to "OK")
}
println(response?.get("status")) // 200
}Вложенные тайм-ауты
Сначала истекают внутренние тайм-ауты. Внешний тайм-аут срабатывает только в том случае, если внутренний не отменяет выполнение раньше — это удобно для разделения тайм-аутов отдельного запроса и всей операции.
import kotlinx.coroutines.*
fun main() = runBlocking {
withTimeoutOrNull(1000) { // global
withTimeoutOrNull(200) { // per-call
delay(300)
println("inner done") // won't print
} ?: println("Inner timed out")
delay(100)
println("outer still running")
}
}TimeoutCancellationException
TimeoutCancellationException является CancellationException, поэтому механизм корутин обрабатывает его как обычную отмену и не передаёт родительской области действия.
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
try {
withTimeout(100) { delay(1000) }
} catch (e: TimeoutCancellationException) {
println("Caught in child: ${e.message}")
}
}
job.join()
println("Parent still running: ${isActive}")
}Очистка ресурсов при тайм-ауте
Используйте finally внутри withTimeout, чтобы освобождать ресурсы даже при истечении времени.
import kotlinx.coroutines.*
fun main() = runBlocking {
val result = withTimeoutOrNull(150) {
try {
println("Opening resource")
delay(300)
"result"
} finally {
println("Closing resource") // always runs
}
}
println("Result: $result")
}Повторная попытка с тайм-аутом
Объединяйте тайм-аут с логикой повторных попыток: выполняйте операцию с отдельным тайм-аутом для каждого attempt и повторяйте её при получении результата null.
import kotlinx.coroutines.*
suspend fun tryFetch(attempt: Int): String? = withTimeoutOrNull(200) {
delay(if (attempt < 3) 300L else 100L) // fails first 2 attempts
"success on attempt $attempt"
}
fun main() = runBlocking {
var result: String? = null
var attempt = 1
while (result == null) {
result = tryFetch(attempt++)
}
println(result)
}withTimeout в ViewModel
В Android ViewModels оборачивайте вызовы репозитория в withTimeoutOrNull внутри viewModelScope.launch, чтобы показывать состояние ошибки при медленных ответах.
import kotlinx.coroutines.*
// Pseudocode pattern:
suspend fun loadUser(): String = withTimeoutOrNull(3000) {
// repo.getUser()
delay(100)
"Alice"
} ?: "Timeout — using cached data"
fun main() = runBlocking { println(loadUser()) }Оговорка о точности
withTimeout зависит от диспетчера корутин. В тестах с использованием TestCoroutineScheduler время виртуальное, и его можно продвигать вручную.
import kotlinx.coroutines.*
// In unit tests with runTest:
// runTest {
// withTimeout(1000) {
// delay(999) // virtual time — completes instantly
// println("done")
// }
// }
fun main() = runBlocking {
println("Use runTest for virtual-time timeout testing")
}Как выбрать между двумя функциями
Используйте withTimeout, когда тайм-аут означает ошибку. Используйте withTimeoutOrNull, когда тайм-аут является ожидаемым результатом, например при отсутствии данных в кэше или необязательной предварительной загрузке.
import kotlinx.coroutines.*
fun main() = runBlocking {
// Mandatory: throw on timeout
// withTimeout(500) { criticalOp() }
// Optional: null on timeout
val cached = withTimeoutOrNull(50) {
delay(200); "fresh"
} ?: "stale"
println(cached)
}Быстрая проверка
Что возвращает withTimeoutOrNull, если выполнение блока превышает ограничение по времени?
Итоги
withTimeout выбрасывает исключение при тайм-ауте, а withTimeoutOrNull возвращает значение null. Обе функции кооперативно отменяют блок и выполняют блоки finally. Используйте finally для очистки ресурсов, а withTimeoutOrNull — когда тайм-аут является ожидаемым результатом.
Изучай Kotlin с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 51
- Уроки
- 203
Часто задаваемые вопросы
Урок «withTimeout и withTimeoutOrNull» бесплатный?
Да — полный текст урока «withTimeout и withTimeoutOrNull» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.
Чему я научусь в уроке «withTimeout и withTimeoutOrNull»?
Ограничивайте время выполнения с помощью обёрток тайм-аута и обрабатывайте TimeoutCancellationException. Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Kotlin Academy?
Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «withTimeout и withTimeoutOrNull»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Kotlin Academy?
Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Кооперативная отмена: isActive и ensureActive
- withTimeout и withTimeoutOrNull
- Очистка с finally и NonCancellable
- Распространение отмены в иерархиях сопрограмм