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

{% hint style="info" %}
Do not use blocking code (e.g. Thread.sleep()) in coroutine
{% endhint %}

### Testing of suspend function

{% code fullWidth="true" %}
```kotlin
@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
}
```
{% endcode %}

## Builders

### Launch

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

```kotlin
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` is used as a bridge between regular and suspending functions, or between the blocking and non-blocking worlds. It works as an adaptor for starting the top-level main coroutine. It is intended primarily to be used in `main()` functions and tests.

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

### async

`async` starts a new coroutine and returns a `Deferred` object. `Deferred` represents a concept known by other names such as `Future` or `Promise`. It stores a computation, but it defers the moment you get the final result; it promises the result sometime in the future.

The main difference between `async` and `launch` is that `launch` is used to start a computation that isn't expected to return a specific result. `launch` returns a `Job` that represents the coroutine. It is possible to wait until it completes by calling `Job.join()`.

`Deferred` is a generic type that extends `Job`. An `async` call can return a `Deferred<Int>` or a `Deferred<CustomType>`, depending on what the lambda returns (the last expression inside the lambda is the result).

To get the result of a coroutine, you can call `await()` on the `Deferred` instance. While waiting for the result, the coroutine that this `await()` is called from is suspended:

```kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
    val deferred: Deferred<Int> = async {
        loadData()
    }
    println("waiting...")
    println(deferred.await())
}

suspend fun loadData(): Int {
    println("loading...")
    delay(1000L)
    println("loaded!")
    return 42
}
```

## Structured concurrency

The coroutine scope is responsible for the structure and parent-child relationships between different coroutines. New coroutines usually need to be started inside a scope.

When `launch`, `async`, or `runBlocking` are used to start a new coroutine, they automatically create the corresponding scope. All of these functions take a lambda with a receiver as an argument, and `CoroutineScope` is the implicit receiver type:

```kotlin
launch { /* this: CoroutineScope */ }
```

```kotlin
import kotlinx.coroutines.*

fun main() = runBlocking { /* this: CoroutineScope */
    launch { /* ... */ }
    // the same as:
    this.launch { /* ... */ }
}
```



## 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.

```kotlin
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.

```kotlin
fun main() = runBlocking {
     val job = launch {
         repeat(1000) {
            delay(1)          // delay knows how to cancel, so cancellation will work
            print(".")
         }
    }
    delay(15)
    job.cancelAndJoin()
    print("Done")
}
```

{% code fullWidth="false" %}
```kotlin
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")
}
```
{% endcode %}

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

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