Coroutines

Context

Coroutines have to be run inside a context.

Suspend function

Suspend functions make sense to start only from coroutine (compilation requirements). And some functions (e.g. delay()) are making sense only inside of suspend functions.

Suspend function can be called from

  • another suspend function

  • from coroutine (This is the way actually to start a "first" suspend function)

Do not use blocking code (e.g. Thread.sleep()) in coroutine

Testing of suspend function

@Test
fun theTest() = runBlocking { // test should become a coroutine to be able to call suspend function
    doWork()    // assuming that doWork is a suspend function
    //assert bla
}

Builders

Launch

Launch creates a coroutine and submit it to the separate thread, main thread will not wait until the coroutine is finished.

launch { 
    delay(1000) // non blocking the thread, it suspends the courutine
    println("")
}

runBlocking

This can be useful to use in tests (to make test a coroutine to be able to invoke suspend function in the test).

runBlocking {        // main thread will wait until this coroutine is finished
    delay(1500)
}

Control

Join

Similar to joining a thread. The calling thread blocks until coroutine is finished.

Launch returns Job, which has join method. We can also check is coroutine is finished.

val job = launch { 
    println("")
}
job.join() // join is a suspend function

Cancel

In order to be cancellable coroutine should be able to check for cancellation.

E.g. delay() function knows how to cancel.

fun main() = runBlocking {
     val job = launch {
         repeat(1000) {
            delay(1)          // delay knows hot to cancel, so cancellation will work
            print(".")
         }
    }
    delay(15)
    job.cancelAndJoin()
    print("Done")
}
fun main() = runBlocking {
     val job = launch {
         repeat(1000) {
            Thread.sleep(1) // this function does not know how to cancel, so cancellation will not work
            print(".")
         }
    }
    delay(15)
    job.cancelAndJoin()
    print("Done")
}

It is possible to check the status itself (it did not work on my machine, but I took it from the course)

fun main() = runBlocking {
     val job = launch {
         repeat(1000) {
             if (!isActive) throw CancellationException()
             sleep(10)
             print(".")
         }
    }
    delay(200)
    job.cancelAndJoin()
    print("Done")
}

Last updated