1<!--- TEST_NAME BasicsGuideTest --> 2 3**Table of contents** 4 5<!--- TOC --> 6 7* [Coroutine Basics](#coroutine-basics) 8 * [Your first coroutine](#your-first-coroutine) 9 * [Bridging blocking and non-blocking worlds](#bridging-blocking-and-non-blocking-worlds) 10 * [Waiting for a job](#waiting-for-a-job) 11 * [Structured concurrency](#structured-concurrency) 12 * [Scope builder](#scope-builder) 13 * [Extract function refactoring](#extract-function-refactoring) 14 * [Coroutines ARE light-weight](#coroutines-are-light-weight) 15 * [Global coroutines are like daemon threads](#global-coroutines-are-like-daemon-threads) 16 17<!--- END --> 18 19## Coroutine Basics 20 21This section covers basic coroutine concepts. 22 23### Your first coroutine 24 25Run the following code: 26 27<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 28 29```kotlin 30import kotlinx.coroutines.* 31 32fun main() { 33 GlobalScope.launch { // launch a new coroutine in background and continue 34 delay(1000L) // non-blocking delay for 1 second (default time unit is ms) 35 println("World!") // print after delay 36 } 37 println("Hello,") // main thread continues while coroutine is delayed 38 Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive 39} 40``` 41 42</div> 43 44> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-basic-01.kt). 45 46You will see the following result: 47 48```text 49Hello, 50World! 51``` 52 53<!--- TEST --> 54 55Essentially, coroutines are light-weight threads. 56They are launched with [launch] _coroutine builder_ in a context of some [CoroutineScope]. 57Here we are launching a new coroutine in the [GlobalScope], meaning that the lifetime of the new 58coroutine is limited only by the lifetime of the whole application. 59 60You can achieve the same result by replacing 61`GlobalScope.launch { ... }` with `thread { ... }`, and `delay(...)` with `Thread.sleep(...)`. 62Try it (don't forget to import `kotlin.concurrent.thread`). 63 64If you start by replacing `GlobalScope.launch` with `thread`, the compiler produces the following error: 65 66``` 67Error: Kotlin: Suspend functions are only allowed to be called from a coroutine or another suspend function 68``` 69 70That is because [delay] is a special _suspending function_ that does not block a thread, but _suspends_ the 71coroutine, and it can be only used from a coroutine. 72 73### Bridging blocking and non-blocking worlds 74 75The first example mixes _non-blocking_ `delay(...)` and _blocking_ `Thread.sleep(...)` in the same code. 76It is easy to lose track of which one is blocking and which one is not. 77Let's be explicit about blocking using the [runBlocking] coroutine builder: 78 79<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 80 81```kotlin 82import kotlinx.coroutines.* 83 84fun main() { 85 GlobalScope.launch { // launch a new coroutine in background and continue 86 delay(1000L) 87 println("World!") 88 } 89 println("Hello,") // main thread continues here immediately 90 runBlocking { // but this expression blocks the main thread 91 delay(2000L) // ... while we delay for 2 seconds to keep JVM alive 92 } 93} 94``` 95 96</div> 97 98> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-basic-02.kt). 99 100<!--- TEST 101Hello, 102World! 103--> 104 105The result is the same, but this code uses only non-blocking [delay]. 106The main thread invoking `runBlocking` _blocks_ until the coroutine inside `runBlocking` completes. 107 108This example can be also rewritten in a more idiomatic way, using `runBlocking` to wrap 109the execution of the main function: 110 111<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 112 113```kotlin 114import kotlinx.coroutines.* 115 116fun main() = runBlocking<Unit> { // start main coroutine 117 GlobalScope.launch { // launch a new coroutine in background and continue 118 delay(1000L) 119 println("World!") 120 } 121 println("Hello,") // main coroutine continues here immediately 122 delay(2000L) // delaying for 2 seconds to keep JVM alive 123} 124``` 125 126</div> 127 128> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt). 129 130<!--- TEST 131Hello, 132World! 133--> 134 135Here `runBlocking<Unit> { ... }` works as an adaptor that is used to start the top-level main coroutine. 136We explicitly specify its `Unit` return type, because a well-formed `main` function in Kotlin has to return `Unit`. 137 138This is also a way to write unit tests for suspending functions: 139 140<!--- INCLUDE 141import kotlinx.coroutines.* 142--> 143 144<div class="sample" markdown="1" theme="idea" data-highlight-only> 145 146```kotlin 147class MyTest { 148 @Test 149 fun testMySuspendingFunction() = runBlocking<Unit> { 150 // here we can use suspending functions using any assertion style that we like 151 } 152} 153``` 154 155</div> 156 157<!--- CLEAR --> 158 159### Waiting for a job 160 161Delaying for a time while another coroutine is working is not a good approach. Let's explicitly 162wait (in a non-blocking way) until the background [Job] that we have launched is complete: 163 164<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 165 166```kotlin 167import kotlinx.coroutines.* 168 169fun main() = runBlocking { 170//sampleStart 171 val job = GlobalScope.launch { // launch a new coroutine and keep a reference to its Job 172 delay(1000L) 173 println("World!") 174 } 175 println("Hello,") 176 job.join() // wait until child coroutine completes 177//sampleEnd 178} 179``` 180 181</div> 182 183> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-basic-04.kt). 184 185<!--- TEST 186Hello, 187World! 188--> 189 190Now the result is still the same, but the code of the main coroutine is not tied to the duration of 191the background job in any way. Much better. 192 193### Structured concurrency 194 195There is still something to be desired for practical usage of coroutines. 196When we use `GlobalScope.launch`, we create a top-level coroutine. Even though it is light-weight, it still 197consumes some memory resources while it runs. If we forget to keep a reference to the newly launched 198coroutine, it still runs. What if the code in the coroutine hangs (for example, we erroneously 199delay for too long), what if we launched too many coroutines and ran out of memory? 200Having to manually keep references to all the launched coroutines and [join][Job.join] them is error-prone. 201 202There is a better solution. We can use structured concurrency in our code. 203Instead of launching coroutines in the [GlobalScope], just like we usually do with threads (threads are always global), 204we can launch coroutines in the specific scope of the operation we are performing. 205 206In our example, we have a `main` function that is turned into a coroutine using the [runBlocking] coroutine builder. 207Every coroutine builder, including `runBlocking`, adds an instance of [CoroutineScope] to the scope of its code block. 208We can launch coroutines in this scope without having to `join` them explicitly, because 209an outer coroutine (`runBlocking` in our example) does not complete until all the coroutines launched 210in its scope complete. Thus, we can make our example simpler: 211 212<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 213 214```kotlin 215import kotlinx.coroutines.* 216 217fun main() = runBlocking { // this: CoroutineScope 218 launch { // launch a new coroutine in the scope of runBlocking 219 delay(1000L) 220 println("World!") 221 } 222 println("Hello,") 223} 224``` 225 226</div> 227 228> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-basic-05.kt). 229 230<!--- TEST 231Hello, 232World! 233--> 234 235### Scope builder 236 237In addition to the coroutine scope provided by different builders, it is possible to declare your own scope using the 238[coroutineScope][_coroutineScope] builder. It creates a coroutine scope and does not complete until all launched children complete. 239 240[runBlocking] and [coroutineScope][_coroutineScope] may look similar because they both wait for their body and all its children to complete. 241The main difference is that the [runBlocking] method _blocks_ the current thread for waiting, 242while [coroutineScope][_coroutineScope] just suspends, releasing the underlying thread for other usages. 243Because of that difference, [runBlocking] is a regular function and [coroutineScope][_coroutineScope] is a suspending function. 244 245It can be demonstrated by the following example: 246 247<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 248 249```kotlin 250import kotlinx.coroutines.* 251 252fun main() = runBlocking { // this: CoroutineScope 253 launch { 254 delay(200L) 255 println("Task from runBlocking") 256 } 257 258 coroutineScope { // Creates a coroutine scope 259 launch { 260 delay(500L) 261 println("Task from nested launch") 262 } 263 264 delay(100L) 265 println("Task from coroutine scope") // This line will be printed before the nested launch 266 } 267 268 println("Coroutine scope is over") // This line is not printed until the nested launch completes 269} 270``` 271 272</div> 273 274> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt). 275 276<!--- TEST 277Task from coroutine scope 278Task from runBlocking 279Task from nested launch 280Coroutine scope is over 281--> 282 283Note that right after the "Task from coroutine scope" message (while waiting for nested launch) 284 "Task from runBlocking" is executed and printed — even though the [coroutineScope][_coroutineScope] is not completed yet. 285 286### Extract function refactoring 287 288Let's extract the block of code inside `launch { ... }` into a separate function. When you 289perform "Extract function" refactoring on this code, you get a new function with the `suspend` modifier. 290This is your first _suspending function_. Suspending functions can be used inside coroutines 291just like regular functions, but their additional feature is that they can, in turn, 292use other suspending functions (like `delay` in this example) to _suspend_ execution of a coroutine. 293 294<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 295 296```kotlin 297import kotlinx.coroutines.* 298 299fun main() = runBlocking { 300 launch { doWorld() } 301 println("Hello,") 302} 303 304// this is your first suspending function 305suspend fun doWorld() { 306 delay(1000L) 307 println("World!") 308} 309``` 310 311</div> 312 313> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt). 314 315<!--- TEST 316Hello, 317World! 318--> 319 320 321But what if the extracted function contains a coroutine builder which is invoked on the current scope? 322In this case, the `suspend` modifier on the extracted function is not enough. Making `doWorld` an extension 323method on `CoroutineScope` is one of the solutions, but it may not always be applicable as it does not make the API clearer. 324The idiomatic solution is to have either an explicit `CoroutineScope` as a field in a class containing the target function 325or an implicit one when the outer class implements `CoroutineScope`. 326As a last resort, [CoroutineScope(coroutineContext)][CoroutineScope()] can be used, but such an approach is structurally unsafe 327because you no longer have control on the scope of execution of this method. Only private APIs can use this builder. 328 329### Coroutines ARE light-weight 330 331Run the following code: 332 333<div class="sample" markdown="1" theme="idea" data-highlight-only> 334 335```kotlin 336import kotlinx.coroutines.* 337 338fun main() = runBlocking { 339 repeat(100_000) { // launch a lot of coroutines 340 launch { 341 delay(5000L) 342 print(".") 343 } 344 } 345} 346``` 347 348</div> 349 350> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-basic-08.kt). 351 352<!--- TEST lines.size == 1 && lines[0] == ".".repeat(100_000) --> 353 354It launches 100K coroutines and, after 5 seconds, each coroutine prints a dot. 355 356Now, try that with threads. What would happen? (Most likely your code will produce some sort of out-of-memory error) 357 358### Global coroutines are like daemon threads 359 360The following code launches a long-running coroutine in [GlobalScope] that prints "I'm sleeping" twice a second and then 361returns from the main function after some delay: 362 363<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 364 365```kotlin 366import kotlinx.coroutines.* 367 368fun main() = runBlocking { 369//sampleStart 370 GlobalScope.launch { 371 repeat(1000) { i -> 372 println("I'm sleeping $i ...") 373 delay(500L) 374 } 375 } 376 delay(1300L) // just quit after delay 377//sampleEnd 378} 379``` 380 381</div> 382 383> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-basic-09.kt). 384 385You can run and see that it prints three lines and terminates: 386 387```text 388I'm sleeping 0 ... 389I'm sleeping 1 ... 390I'm sleeping 2 ... 391``` 392 393<!--- TEST --> 394 395Active coroutines that were launched in [GlobalScope] do not keep the process alive. They are like daemon threads. 396 397<!--- MODULE kotlinx-coroutines-core --> 398<!--- INDEX kotlinx.coroutines --> 399[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html 400[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html 401[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html 402[delay]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html 403[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html 404[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html 405[Job.join]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html 406[_coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html 407[CoroutineScope()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope.html 408<!--- END --> 409 410 411