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