aboutsummaryrefslogtreecommitdiffstats
path: root/docs/topics/coroutines-basics.md
blob: 5d9d0e6db1342753307bf04a5f5de66d350a2be8 (plain)
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
<!--- TEST_NAME BasicsGuideTest -->

[//]: # (title: Coroutines basics)

This section covers basic coroutine concepts.

## Your first coroutine

A _coroutine_ is an instance of suspendable computation. It is conceptually similar to a thread, in the sense that it 
takes a block of code to run that works concurrently with the rest of the code.
However, a coroutine is not bound to any particular thread. It may suspend its execution in one thread and resume in another one. 

Coroutines can be thought of as light-weight threads, but there is a number
of important differences that make their real-life usage very different from threads.

Run the following code to get to your first working coroutine:

```kotlin
import kotlinx.coroutines.*

//sampleStart
fun main() = runBlocking { // this: CoroutineScope
    launch { // launch a new coroutine and continue
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println("World!") // print after delay
    }
    println("Hello") // main coroutine continues while a previous one is delayed
}
//sampleEnd
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}

> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-01.kt).
>
{type="note"}

You will see the following result:

```text
Hello
World!
```

<!--- TEST -->

Let's dissect what this code does. 

[launch] is a _coroutine builder_. It launches a new coroutine concurrently with 
the rest of the code, which continues to work independently. That's why `Hello` has been printed first.

[delay] is a special _suspending function_. It _suspends_ the coroutine for a specific time. Suspending a coroutine
does not _block_ the underlying thread, but allows other coroutines to run and use the underlying thread for 
their code. 

[runBlocking] is also a coroutine builder that bridges the non-coroutine world of a regular `fun main()` and 
the code with coroutines inside of `runBlocking { ... }` curly braces. This is highlighted in an IDE by
`this: CoroutineScope` hint right after the `runBlocking` opening curly brace.

If you remove or forget `runBlocking` in this code, you'll get an error on the [launch] call, since `launch`
is declared only in the [CoroutineScope]:

```Plain Text
Unresolved reference: launch
```

The name of `runBlocking` means that the thread that runs it (in this case &mdash; the main thread) gets _blocked_ for 
the duration of the call, until all the coroutines inside `runBlocking { ... }` complete their execution. You will 
often see `runBlocking` used like that at the very top-level of the application and quite rarely inside the real code, 
as threads are expensive resources and blocking them is inefficient and is often not desired. 

### Structured concurrency

Coroutines follow a principle of 
**structured concurrency** which means that new coroutines can be only launched in a specific [CoroutineScope]
which delimits the lifetime of the coroutine. The above example shows that [runBlocking] establishes the corresponding
scope and that is why the previous example waits until `World!` is printed after a second's delay and only then exits.

In the real application, you will be launching a lot of coroutines. Structured concurrency ensures that they are not
lost and do not leak. An outer scope cannot complete until all its children coroutines complete. 
Structured concurrency also ensures that any errors in the code are properly reported and are never lost.  

## 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 the `suspend` modifier.
This 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.

```kotlin
import kotlinx.coroutines.*

//sampleStart
fun main() = runBlocking { // this: CoroutineScope
    launch { doWorld() }
    println("Hello")
}

// this is your first suspending function
suspend fun doWorld() {
    delay(1000L)
    println("World!")
}
//sampleEnd
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}

> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-02.kt).
>
{type="note"}

<!--- TEST
Hello
World!
-->

## Scope builder

In addition to the coroutine scope provided by different builders, it is possible to declare your own scope using the
[coroutineScope][_coroutineScope] builder. It creates a coroutine scope and does not complete until all launched children complete.

[runBlocking] and [coroutineScope][_coroutineScope] builders may look similar because they both wait for their body and all its children to complete.
The main difference is that the [runBlocking] method _blocks_ the current thread for waiting,
while [coroutineScope][_coroutineScope] just suspends, releasing the underlying thread for other usages.
Because of that difference, [runBlocking] is a regular function and [coroutineScope][_coroutineScope] is a suspending function.

You can use `coroutineScope` from any suspending function. 
For example, you can move the concurrent printing of `Hello` and `World` into a `suspend fun doWorld()` function: 

```kotlin
import kotlinx.coroutines.*

//sampleStart
fun main() = runBlocking {
    doWorld()
}

suspend fun doWorld() = coroutineScope {  // this: CoroutineScope
    launch {
        delay(1000L)
        println("World!")
    }
    println("Hello")
}
//sampleEnd
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}

> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt).
>
{type="note"}

This code also prints:

```text
Hello
World!
```

<!--- TEST -->

## Scope builder and concurrency

A [coroutineScope][_coroutineScope] builder can be used inside any suspending function to perform multiple concurrent operations.
Let's launch two concurrent coroutines inside a `doWorld` suspending function:

```kotlin
import kotlinx.coroutines.*

//sampleStart
// Sequentially executes doWorld followed by "Done"
fun main() = runBlocking {
    doWorld()
    println("Done")
}

// Concurrently executes both sections
suspend fun doWorld() = coroutineScope { // this: CoroutineScope
    launch {
        delay(2000L)
        println("World 2")
    }
    launch {
        delay(1000L)
        println("World 1")
    }
    println("Hello")
}
//sampleEnd
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}

> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-04.kt).
>
{type="note"}

Both pieces of code inside `launch { ... }` blocks execute _concurrently_, with 
`World 1` printed first, after a second from start, and `World 2` printed next, after two seconds from start.
A [coroutineScope][_coroutineScope] in `doWorld` completes only after both are complete, so `doWorld` returns and 
allows `Done` string to be printed only after that:

```text
Hello
World 1
World 2
Done
```

<!--- TEST -->

## An explicit job

A [launch] coroutine builder returns a [Job] object that is a handle to the launched coroutine and can be 
used to explicitly wait for its completion. For example, you can wait for completion of the child coroutine
and then print "Done" string:

```kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
//sampleStart
    val job = launch { // launch a new coroutine and keep a reference to its Job
        delay(1000L)
        println("World!")
    }
    println("Hello")
    job.join() // wait until child coroutine completes
    println("Done") 
//sampleEnd    
}
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}

> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-05.kt).
>
{type="note"}

This code produces: 

```text
Hello
World!
Done
```

<!--- TEST -->

## Coroutines ARE light-weight

Run the following code:

```kotlin
import kotlinx.coroutines.*

//sampleStart
fun main() = runBlocking {
    repeat(100_000) { // launch a lot of coroutines
        launch {
            delay(5000L)
            print(".")
        }
    }
}
//sampleEnd
```

> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt).
>
{type="note"}

<!--- TEST lines.size == 1 && lines[0] == ".".repeat(100_000) -->

It launches 100K coroutines and, after 5 seconds, each coroutine prints a dot. 

Now, try that with threads (remove `runBlocking`, replace `launch` with `thread`, and replace `delay` with `Thread.sleep`). 
What would happen? (Most likely your code will produce some sort of out-of-memory error)

<!--- MODULE kotlinx-coroutines-core -->
<!--- INDEX kotlinx.coroutines -->

[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.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
[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
[_coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html
[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html

<!--- END -->