Skip to content

Commit 4518112

Browse files
committed
docs: Catch description is improved, launchIn example added
1 parent 8604840 commit 4518112

File tree

4 files changed

+111
-23
lines changed

4 files changed

+111
-23
lines changed

docs/flow.md

+61-22
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ Running this code produces:
661661
[main @coroutine#1] Collected 3
662662
```
663663

664-
<!--- TEST -->
664+
<!--- TEST FLEXIBLE_THREAD -->
665665

666666
Since `foo().collect` is called from the main thread, the body of `foo`'s flow is also called in the main thread.
667667
This is a perfect default for fast-running or asynchronous code that does not care about the execution context and
@@ -1532,12 +1532,13 @@ Caught java.lang.IllegalStateException: Collected 2
15321532

15331533
### Launching flow
15341534

1535-
It is convenient to use flows to represents asynchronous events that are coming from some source.
1535+
It is convenient to use flows to represent asynchronous events that are coming from some source.
15361536
In this case we need an analogue of `addEventListener` function that registers a piece of code with reaction
1537-
on incoming events and continues further work. That is where [launchIn] operator comes in handy
1538-
together with [onEach] operator that we've seen previously.
1539-
1540-
Consider the following example:
1537+
on incoming events and continues further work. The [onEach] operator can serve this role.
1538+
However, `onEach` is an intermediate operator. We also need a terminal operator to collect the flow.
1539+
Otherwise, just calling `onEach` has no effect.
1540+
1541+
If we use [collect] terminal operator after `oneEach` then code after it waits until the flow is collected:
15411542

15421543
<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
15431544

@@ -1551,10 +1552,9 @@ fun events(): Flow<Int> = (1..3).asFlow().onEach { delay(100) }
15511552

15521553
fun main() = runBlocking<Unit> {
15531554
events()
1554-
.onEach { event ->
1555-
println("Event: $event")
1556-
}
1557-
.launchIn(this)
1555+
.onEach { event -> println("Event: $event") }
1556+
.collect() // <--- Collecting the flow waits
1557+
println("Done")
15581558
}
15591559
//sampleEnd
15601560
```
@@ -1563,29 +1563,68 @@ fun main() = runBlocking<Unit> {
15631563

15641564
> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-31.kt).
15651565
1566-
It prints:
1566+
As you can see, it prints:
15671567

15681568
```text
15691569
Event: 1
15701570
Event: 2
15711571
Event: 3
1572+
Done
15721573
```
15731574

15741575
<!--- TEST -->
1576+
1577+
Here [launchIn] terminal operator comes in handy. Replacing `collect` with `launchIn` we can
1578+
launch collection of the flow in a separate coroutine, so that execution of further code
1579+
immediately continues:
1580+
1581+
<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
1582+
1583+
```kotlin
1584+
import kotlinx.coroutines.*
1585+
import kotlinx.coroutines.flow.*
1586+
1587+
// Imitate a flow of events
1588+
fun events(): Flow<Int> = (1..3).asFlow().onEach { delay(100) }
1589+
1590+
//sampleStart
1591+
fun main() = runBlocking<Unit> {
1592+
events()
1593+
.onEach { event -> println("Event: $event") }
1594+
.launchIn(this) // <--- Launching the flow in a separate coroutine
1595+
println("Done")
1596+
}
1597+
//sampleEnd
1598+
```
1599+
1600+
</div>
1601+
1602+
> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-32.kt).
1603+
1604+
It prints:
1605+
1606+
```text
1607+
Done
1608+
Event: 1
1609+
Event: 2
1610+
Event: 3
1611+
```
1612+
1613+
<!--- TEST -->
1614+
1615+
The required parameter to `launchIn` must specify a [CoroutineScope] in which the coroutine to collect the flow is
1616+
launched. In the above example this scope comes from [runBlocking]
1617+
coroutine builder, so while the flow is running this [runBlocking] scope waits for completion of its child coroutine
1618+
and keeps the main function from returning and terminating this example.
15751619

1576-
In this example the body of `onEach { ... }` operator works as an event listener. However, [onEach] operator
1577-
is intermediate and calling this operator by itself does not have any effect. A terminal operator is needed
1578-
to collect the flow and [launchIn] serves this purpose. The required parameter to `launchIn` must specify
1579-
a [CoroutineScope] in which this flow is collected. In the above example this scope comes from [runBlocking]
1580-
coroutine builder, so while the flow is not over, the [runBlocking] waits and keeps the main function from
1581-
returning and terminating this example. In real applications a scope is going to come from some entity with a limited
1582-
lifetime and as soon as the lifetime of this entity is terminated the corresponding scope is cancelled, cancelling
1583-
collection of the corresponding flow. This way the pair of `onEach { ... }.collectIn` calls works
1584-
like `addEventListener`, but there is no need for a corresponding `removeEventListener` function, as cancellation and
1585-
structured concurrency serve this purpose.
1620+
In real applications a scope is going to come from some entity with a limited
1621+
lifetime. As soon as the lifetime of this entity is terminated the corresponding scope is cancelled, cancelling
1622+
collection of the corresponding flow. This way the pair of `onEach { ... }.collectIn(scope)` works
1623+
like `addEventListener`. However, there is no need for the corresponding `removeEventListener` function,
1624+
as cancellation and structured concurrency serve this purpose.
15861625

15871626
Note, that [launchIn] also returns a [Job] which can be used to [cancel][Job.cancel] the corresponding flow collection
1588-
only without cancelling the whole scope.
1627+
coroutine only without cancelling the whole scope.
15891628

15901629
<!-- stdlib references -->
15911630

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
6+
package kotlinx.coroutines.guide.flow31
7+
8+
import kotlinx.coroutines.*
9+
import kotlinx.coroutines.flow.*
10+
11+
// Imitate a flow of events
12+
fun events(): Flow<Int> = (1..3).asFlow().onEach { delay(100) }
13+
14+
fun main() = runBlocking<Unit> {
15+
events()
16+
.onEach { event -> println("Event: $event") }
17+
.collect() // <--- Collecting the flow waits
18+
println("Done")
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
6+
package kotlinx.coroutines.guide.flow32
7+
8+
import kotlinx.coroutines.*
9+
import kotlinx.coroutines.flow.*
10+
11+
// Imitate a flow of events
12+
fun events(): Flow<Int> = (1..3).asFlow().onEach { delay(100) }
13+
14+
fun main() = runBlocking<Unit> {
15+
events()
16+
.onEach { event -> println("Event: $event") }
17+
.launchIn(this) // <--- Launching the flow in a separate coroutine
18+
println("Done")
19+
}

kotlinx-coroutines-core/jvm/test/guide/test/FlowGuideTest.kt

+12-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ class FlowGuideTest {
135135

136136
@Test
137137
fun testKotlinxCoroutinesGuideFlow13() {
138-
test("KotlinxCoroutinesGuideFlow13") { kotlinx.coroutines.guide.flow13.main() }.verifyLines(
138+
test("KotlinxCoroutinesGuideFlow13") { kotlinx.coroutines.guide.flow13.main() }.verifyLinesFlexibleThread(
139139
"[main @coroutine#1] Started foo flow",
140140
"[main @coroutine#1] Collected 1",
141141
"[main @coroutine#1] Collected 2",
@@ -324,6 +324,17 @@ class FlowGuideTest {
324324
@Test
325325
fun testKotlinxCoroutinesGuideFlow31() {
326326
test("KotlinxCoroutinesGuideFlow31") { kotlinx.coroutines.guide.flow31.main() }.verifyLines(
327+
"Event: 1",
328+
"Event: 2",
329+
"Event: 3",
330+
"Done"
331+
)
332+
}
333+
334+
@Test
335+
fun testKotlinxCoroutinesGuideFlow32() {
336+
test("KotlinxCoroutinesGuideFlow32") { kotlinx.coroutines.guide.flow32.main() }.verifyLines(
337+
"Done",
327338
"Event: 1",
328339
"Event: 2",
329340
"Event: 3"

0 commit comments

Comments
 (0)