Kotlin Academy · Lekcja

withTimeout i withTimeoutOrNull

Ograniczaj czas wykonywania za pomocą opakowań timeout i obsługuj TimeoutCancellationException.

Lekcja 2 z 413 kroki

withTimeout i withTimeoutOrNull to bezpłatna lekcja Kotlin Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Kotlin Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Kotlin Academy zawiera 4 lekcji w sumie.

Po co stosować limity czasu?

Korutyny mogą zawiesić się na czas nieokreślony podczas operacji sieciowych lub wejścia-wyjścia. withTimeout anuluje blok, jeśli nie zakończy się on w podanym czasie wyrażonym w milisekundach.

import kotlinx.coroutines.*
fun main() = runBlocking {
    try {
        withTimeout(200) {
            delay(1000)  // simulates slow network
            println("This never prints")
        }
    } catch (e: TimeoutCancellationException) {
        println("Timed out!")
    }
}

Podstawy withTimeout

withTimeout(millis) { ... } zgłasza TimeoutCancellationException (podklasę CancellationException), jeśli wykonanie bloku przekroczy limit.

import kotlinx.coroutines.*
suspend fun fetchData(): String {
    delay(100)
    return "data"
}
fun main() = runBlocking {
    val result = withTimeout(500) {
        fetchData()
    }
    println(result) // data
}

withTimeoutOrNull

withTimeoutOrNull zwraca null po przekroczeniu limitu zamiast zgłaszać wyjątek, dzięki czemu łatwiej obsługiwać przekroczenie limitu jako normalny przebieg programu.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val result: String? = withTimeoutOrNull(200) {
        delay(1000)
        "done"
    }
    println(result ?: "Timeout — using default")
}

Limit czasu a wartość zwracana

Po pomyślnym wykonaniu obie funkcje zwracają wartość ostatniego wyrażenia w bloku.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val response = withTimeoutOrNull(500) {
        delay(100)
        mapOf("status" to 200, "body" to "OK")
    }
    println(response?.get("status")) // 200
}

Zagnieżdżanie limitów czasu

Wewnętrzne limity czasu wygasają jako pierwsze. Zewnętrzny limit zadziała tylko wtedy, gdy wewnętrzny nie anuluje wykonania wcześniej — jest to przydatne w przypadku limitów dla poszczególnych żądań oraz limitu globalnego.

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 jest obiektem typu CancellationException, dlatego mechanizm korutyn traktuje go jako normalne anulowanie i nie propaguje go do nadrzędnego zakresu.

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}")
}

Zwalnianie zasobów po przekroczeniu limitu czasu

Wewnątrz withTimeout należy używać finally, aby zwolnić zasoby nawet po upływie limitu czasu.

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")
}

Ponawianie prób z limitem czasu

Połącz limit czasu z logiką ponawiania prób: wykonuj operację z limitem czasu dla każdej próby i ponawiaj ją po otrzymaniu wyniku 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 w ViewModel

W Androidowych obiektach ViewModel wywołania repozytorium należy opakować w withTimeoutOrNull wewnątrz viewModelScope.launch, aby przy wolnych odpowiedziach wyświetlić stan błędu.

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()) }

Uwaga dotycząca dokładności

withTimeout korzysta z dyspozytora korutyn. W testach używających TestCoroutineScheduler czas jest wirtualny i można go przesuwać ręcznie.

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")
}

Wybór między obiema funkcjami

Użyj withTimeout, gdy przekroczenie limitu czasu jest błędem. Użyj withTimeoutOrNull, gdy jest ono oczekiwanym wynikiem (np. brak danych w pamięci podręcznej lub opcjonalne wstępne pobieranie).

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)
}

Szybkie sprawdzenie

Co zwraca withTimeoutOrNull, gdy wykonanie bloku przekroczy limit czasu?

Podsumowanie

withTimeout zgłasza wyjątek po przekroczeniu limitu czasu, a withTimeoutOrNull zwraca null. Obie funkcje kooperacyjnie anulują blok i wykonują bloki finally. Używaj finally do zwalniania zasobów, a withTimeoutOrNull, gdy przekroczenie limitu czasu jest oczekiwanym wynikiem.

Bezpłatny start

Ucz się Kotlin dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
51
Lekcje
203

Często zadawane pytania

Czy lekcja „withTimeout i withTimeoutOrNull” jest bezpłatna?

Tak — pełny tekst „withTimeout i withTimeoutOrNull” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Kotlin Academy, przejdź na CoddyKit PRO. Kurs Kotlin Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „withTimeout i withTimeoutOrNull”?

Ograniczaj czas wykonywania za pomocą opakowań timeout i obsługuj TimeoutCancellationException. Ćwiczysz Kotlin Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Kotlin Academy?

Nie wymagamy żadnego doświadczenia. Kotlin Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „withTimeout i withTimeoutOrNull”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Kotlin Academy?

Tak. Każda lekcja Kotlin Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Kooperatywne anulowanie: isActive i ensureActive
  2. withTimeout i withTimeoutOrNull
  3. Sprzątanie za pomocą finally i NonCancellable
  4. Propagacja anulowania w hierarchiach korutyn
← Powrót do Kotlin Academy