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