@@ -661,7 +661,7 @@ Running this code produces:
661
661
[main @coroutine#1] Collected 3
662
662
```
663
663
664
- <!-- - TEST -->
664
+ <!-- - TEST FLEXIBLE_THREAD -->
665
665
666
666
Since ` foo().collect ` is called from the main thread, the body of ` foo ` 's flow is also called in the main thread.
667
667
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
1532
1532
1533
1533
### Launching flow
1534
1534
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.
1536
1536
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:
1541
1542
1542
1543
<div class =" sample " markdown =" 1 " theme =" idea " data-min-compiler-version =" 1.3 " >
1543
1544
@@ -1551,10 +1552,9 @@ fun events(): Flow<Int> = (1..3).asFlow().onEach { delay(100) }
1551
1552
1552
1553
fun main () = runBlocking<Unit > {
1553
1554
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" )
1558
1558
}
1559
1559
// sampleEnd
1560
1560
```
@@ -1563,29 +1563,68 @@ fun main() = runBlocking<Unit> {
1563
1563
1564
1564
> You can get full code [ here] ( ../kotlinx-coroutines-core/jvm/test/guide/example-flow-31.kt ) .
1565
1565
1566
- It prints:
1566
+ As you can see, it prints:
1567
1567
1568
1568
``` text
1569
1569
Event: 1
1570
1570
Event: 2
1571
1571
Event: 3
1572
+ Done
1572
1573
```
1573
1574
1574
1575
<!-- - 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.
1575
1619
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.
1586
1625
1587
1626
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.
1589
1628
1590
1629
<!-- stdlib references -->
1591
1630
0 commit comments