• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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