You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* Purity is a way too polluted word and it is hard to tell what it means in Flow
* Rename "purity" to "context preservation"
* Prohibit changing the context, ignore only Job and CoroutineId (for debug mode) in SafeCollector
* Reword documentation, add more samples
* Add explanation for deprecated Rx-like methods
Copy file name to clipboardExpand all lines: kotlinx-coroutines-core/common/src/flow/Flow.kt
+37-15
Original file line number
Diff line number
Diff line change
@@ -7,30 +7,29 @@ package kotlinx.coroutines.flow
7
7
importkotlinx.coroutines.*
8
8
9
9
/**
10
-
* A cold asynchronous stream of the data, that emits from zero to N (where N can be unbounded)
11
-
* values and completes normally or with an exception.
10
+
* A cold asynchronous stream of the data, that emits from zero to N (where N can be unbounded) values and completes normally or with an exception.
12
11
*
13
-
* All transformations on the flow, such as [map] and [filter] do not trigger flow collection or execution, only
14
-
* terminal operators (e.g. [single])do trigger it.
12
+
* All transformations on the flow, such as [map] and [filter] do not trigger flow collection or execution, only terminal operators (e.g. [single]) do trigger it.
15
13
*
16
-
* Flow can be collected in a suspending manner, without actual blocking using [collect] extension that will complete normally or exceptionally:
14
+
* Flow can be collected in a suspending manner, without actual blocking, using [collect] extension that will complete normally or exceptionally:
17
15
* ```
18
16
* try {
19
-
* flow.collect { value ->
20
-
* println("Received $value")
21
-
* }
17
+
* flow.collect { value ->
18
+
* println("Received $value")
19
+
* }
22
20
* } catch (e: Exception) {
23
-
* println("Flow has thrown an exception: $e")
21
+
* println("Flow has thrown an exception: $e")
24
22
* }
25
23
* ```
26
24
* Additionally, the library provides a rich set of terminal operators such as [single], [reduce] and others.
27
25
*
28
26
* Flow does not carry information whether it is a cold stream (that can be collected multiple times and
29
-
* triggers its evaluation every time collection is executed) or hot one, but conventionally flow represents a cold stream.
30
-
* Transitions between hot and cold streams are supported via channels and corresponding API: [flowViaChannel], [broadcastIn], [produceIn].
27
+
* triggers its evaluation every time [collect] is executed) or a hot one, but conventionally flow represents a cold stream.
28
+
* Transitions between hot and cold streams are supported via channels and the corresponding API: [flowViaChannel], [broadcastIn], [produceIn].
31
29
*
32
-
* Flow is a **pure** concept: it encapsulates its own execution context and never propagates it to the downstream, thus making
30
+
* Flow has a context preserving property: it encapsulates its own execution context and never propagates or leaks it to the downstream, thus making
33
31
* reasoning about execution context of particular transformations or terminal operations trivial.
32
+
*
34
33
* There are two ways of changing the flow's context: [flowOn][Flow.flowOn] and [flowWith][Flow.flowWith].
35
34
* The former changes the upstream context ("everything above the flowOn operator") while the latter
36
35
* changes the context of the flow within [flowWith] body. For additional information refer to these operators documentation.
@@ -41,10 +40,10 @@ import kotlinx.coroutines.*
41
40
* .map { it + 1 } // Will be executed in ctx_1
42
41
* .flowOn(ctx_1) // Changes upstream context: flowOf and map
43
42
*
44
-
* // Now we have flow that is pure: it is executed somewhere but this information is encapsulated in the flow itself
43
+
* // Now we have flow that is context-preserving: it is executed somewhere but this information is encapsulated in the flow itself
45
44
*
46
-
* val filtered = flow
47
-
* .filter { it == 3 } // Pure operator without a context
45
+
* val filtered = flow // ctx_1 is inaccessible
46
+
* .filter { it == 3 } // Pure operator without a context yet
48
47
*
49
48
* withContext(Dispatchers.Main) {
50
49
* // All not encapsulated operators will be executed in Main: filter and single
@@ -53,6 +52,29 @@ import kotlinx.coroutines.*
53
52
* }
54
53
* ```
55
54
*
55
+
* From the implementation point of view it means that all intermediate operators on [Flow] should use the following constraint:
56
+
* If one wants to separate collection or emission into multiple coroutines, it should use [coroutineScope] or [supervisorScope] and
57
+
* is not allowed to modify coroutines context:
58
+
* ```
59
+
* fun <T> Flow<T>.buffer(bufferSize: Int): Flow<T> = flow {
60
+
* coroutineScope { // coroutine scope is necessary, withContext is prohibited
61
+
* val channel = Channel<T>(bufferSize)
62
+
* // GlobalScope.launch { is prohibited
63
+
* // launch(Dispatchers.IO) { is prohibited
64
+
* launch { // is OK
65
+
* collect { value ->
66
+
* channel.send(value)
67
+
* }
68
+
* channel.close()
69
+
* }
70
+
*
71
+
* for (i in channel) {
72
+
* emit(i)
73
+
* }
74
+
* }
75
+
* }
76
+
* ```
77
+
*
56
78
* Flow is [Reactive Streams](http://www.reactive-streams.org/) compliant, you can safely interop it with reactive streams using [Flow.asPublisher] and [Publisher.asFlow] from
0 commit comments