|
| 1 | +package kotlinx.coroutines.flow |
| 2 | + |
| 3 | +import kotlinx.coroutines.* |
| 4 | +import kotlinx.coroutines.flow.internal.* |
| 5 | +import kotlin.coroutines.* |
| 6 | + |
| 7 | +/** |
| 8 | + * An asynchronous data stream that sequentially emits values and completes normally or with an exception. |
| 9 | + * |
| 10 | + * _Intermediate operators_ on the flow such as [map], [filter], [take], [zip], etc are functions that are |
| 11 | + * applied to the _upstream_ flow or flows and return a _downstream_ flow where further operators can be applied to. |
| 12 | + * Intermediate operations do not execute any code in the flow and are not suspending functions themselves. |
| 13 | + * They only set up a chain of operations for future execution and quickly return. |
| 14 | + * This is known as a _cold flow_ property. |
| 15 | + * |
| 16 | + * _Terminal operators_ on the flow are either suspending functions such as [collect], [single], [reduce], [toList], etc. |
| 17 | + * or [launchIn] operator that starts collection of the flow in the given scope. |
| 18 | + * They are applied to the upstream flow and trigger execution of all operations. |
| 19 | + * Execution of the flow is also called _collecting the flow_ and is always performed in a suspending manner |
| 20 | + * without actual blocking. Terminal operators complete normally or exceptionally depending on successful or failed |
| 21 | + * execution of all the flow operations in the upstream. The most basic terminal operator is [collect], for example: |
| 22 | + * |
| 23 | + * ``` |
| 24 | + * try { |
| 25 | + * flow.collect { value -> |
| 26 | + * println("Received $value") |
| 27 | + * } |
| 28 | + * } catch (e: Exception) { |
| 29 | + * println("The flow has thrown an exception: $e") |
| 30 | + * } |
| 31 | + * ``` |
| 32 | + * |
| 33 | + * By default, flows are _sequential_ and all flow operations are executed sequentially in the same coroutine, |
| 34 | + * with an exception for a few operations specifically designed to introduce concurrency into flow |
| 35 | + * execution such as [buffer] and [flatMapMerge]. See their documentation for details. |
| 36 | + * |
| 37 | + * The `Flow` interface does not carry information whether a flow is a _cold_ stream that can be collected repeatedly and |
| 38 | + * triggers execution of the same code every time it is collected, or if it is a _hot_ stream that emits different |
| 39 | + * values from the same running source on each collection. Usually flows represent _cold_ streams, but |
| 40 | + * there is a [SharedFlow] subtype that represents _hot_ streams. In addition to that, any flow can be turned |
| 41 | + * into a _hot_ one by the [stateIn] and [shareIn] operators, or by converting the flow into a hot channel |
| 42 | + * via the [produceIn] operator. |
| 43 | + * |
| 44 | + * ### Flow builders |
| 45 | + * |
| 46 | + * There are the following basic ways to create a flow: |
| 47 | + * |
| 48 | + * - [flowOf(...)][flowOf] functions to create a flow from a fixed set of values. |
| 49 | + * - [asFlow()][asFlow] extension functions on various types to convert them into flows. |
| 50 | + * - [flow { ... }][flow] builder function to construct arbitrary flows from |
| 51 | + * sequential calls to [emit][FlowCollector.emit] function. |
| 52 | + * - [channelFlow { ... }][channelFlow] builder function to construct arbitrary flows from |
| 53 | + * potentially concurrent calls to the [send][kotlinx.coroutines.channels.SendChannel.send] function. |
| 54 | + * - [MutableStateFlow] and [MutableSharedFlow] define the corresponding constructor functions to create |
| 55 | + * a _hot_ flow that can be directly updated. |
| 56 | + * |
| 57 | + * ### Flow constraints |
| 58 | + * |
| 59 | + * All implementations of the `Flow` interface must adhere to two key properties described in detail below: |
| 60 | + * |
| 61 | + * - Context preservation. |
| 62 | + * - Exception transparency. |
| 63 | + * |
| 64 | + * These properties ensure the ability to perform local reasoning about the code with flows and modularize the code |
| 65 | + * in such a way that upstream flow emitters can be developed separately from downstream flow collectors. |
| 66 | + * A user of a flow does not need to be aware of implementation details of the upstream flows it uses. |
| 67 | + * |
| 68 | + * ### Context preservation |
| 69 | + * |
| 70 | + * The flow has a context preservation property: it encapsulates its own execution context and never propagates or leaks |
| 71 | + * it downstream, thus making reasoning about the execution context of particular transformations or terminal |
| 72 | + * operations trivial. |
| 73 | + * |
| 74 | + * There is only one way to change the context of a flow: the [flowOn][Flow.flowOn] operator |
| 75 | + * that changes the upstream context ("everything above the `flowOn` operator"). |
| 76 | + * For additional information refer to its documentation. |
| 77 | + * |
| 78 | + * This reasoning can be demonstrated in practice: |
| 79 | + * |
| 80 | + * ``` |
| 81 | + * val flowA = flowOf(1, 2, 3) |
| 82 | + * .map { it + 1 } // Will be executed in ctxA |
| 83 | + * .flowOn(ctxA) // Changes the upstream context: flowOf and map |
| 84 | + * |
| 85 | + * // Now we have a context-preserving flow: it is executed somewhere but this information is encapsulated in the flow itself |
| 86 | + * |
| 87 | + * val filtered = flowA // ctxA is encapsulated in flowA |
| 88 | + * .filter { it == 3 } // Pure operator without a context yet |
| 89 | + * |
| 90 | + * withContext(Dispatchers.Main) { |
| 91 | + * // All non-encapsulated operators will be executed in Main: filter and single |
| 92 | + * val result = filtered.single() |
| 93 | + * myUi.text = result |
| 94 | + * } |
| 95 | + * ``` |
| 96 | + * |
| 97 | + * From the implementation point of view, it means that all flow implementations should |
| 98 | + * only emit from the same coroutine context. |
| 99 | + * This constraint is efficiently enforced by the default [flow] builder. |
| 100 | + * The [flow] builder should be used if the flow implementation does not start any coroutines. |
| 101 | + * Its implementation prevents most of the development mistakes: |
| 102 | + * |
| 103 | + * ``` |
| 104 | + * val myFlow = flow { |
| 105 | + * // GlobalScope.launch { // is prohibited |
| 106 | + * // launch(Dispatchers.IO) { // is prohibited |
| 107 | + * // withContext(CoroutineName("myFlow")) { // is prohibited |
| 108 | + * emit(1) // OK |
| 109 | + * coroutineScope { |
| 110 | + * emit(2) // OK -- still the same coroutine |
| 111 | + * } |
| 112 | + * } |
| 113 | + * ``` |
| 114 | + * |
| 115 | + * Use [channelFlow] if the collection and emission of a flow are to be separated into multiple coroutines. |
| 116 | + * It encapsulates all the context preservation work and allows you to focus on your |
| 117 | + * domain-specific problem, rather than invariant implementation details. |
| 118 | + * It is possible to use any combination of coroutine builders from within [channelFlow]. |
| 119 | + * |
| 120 | + * If you are looking for performance and are sure that no concurrent emits and context jumps will happen, |
| 121 | + * the [flow] builder can be used alongside a [coroutineScope] or [supervisorScope] instead: |
| 122 | + * - Scoped primitive should be used to provide a [CoroutineScope]. |
| 123 | + * - Changing the context of emission is prohibited, no matter whether it is `withContext(ctx)` or |
| 124 | + * a builder argument (e.g. `launch(ctx)`). |
| 125 | + * - Collecting another flow from a separate context is allowed, but it has the same effect as |
| 126 | + * applying the [flowOn] operator to that flow, which is more efficient. |
| 127 | + * |
| 128 | + * ### Exception transparency |
| 129 | + * |
| 130 | + * When `emit` or `emitAll` throws, the Flow implementations must immediately stop emitting new values and finish with an exception. |
| 131 | + * For diagnostics or application-specific purposes, the exception may be different from the one thrown by the emit operation, |
| 132 | + * suppressing the original exception as discussed below. |
| 133 | + * If there is a need to emit values after the downstream failed, please use the [catch][Flow.catch] operator. |
| 134 | + * |
| 135 | + * The [catch][Flow.catch] operator only catches upstream exceptions, but passes |
| 136 | + * all downstream exceptions. Similarly, terminal operators like [collect][Flow.collect] |
| 137 | + * throw any unhandled exceptions that occur in their code or in upstream flows, for example: |
| 138 | + * |
| 139 | + * ``` |
| 140 | + * flow { emitData() } |
| 141 | + * .map { computeOne(it) } |
| 142 | + * .catch { ... } // catches exceptions in emitData and computeOne |
| 143 | + * .map { computeTwo(it) } |
| 144 | + * .collect { process(it) } // throws exceptions from process and computeTwo |
| 145 | + * ``` |
| 146 | + * The same reasoning can be applied to the [onCompletion] operator that is a declarative replacement for the `finally` block. |
| 147 | + * |
| 148 | + * All exception-handling Flow operators follow the principle of exception suppression: |
| 149 | + * |
| 150 | + * If the upstream flow throws an exception during its completion when the downstream exception has been thrown, |
| 151 | + * the downstream exception becomes superseded and suppressed by the upstream exception, being a semantic |
| 152 | + * equivalent of throwing from `finally` block. However, this doesn't affect the operation of the exception-handling operators, |
| 153 | + * which consider the downstream exception to be the root cause and behave as if the upstream didn't throw anything. |
| 154 | + * |
| 155 | + * Failure to adhere to the exception transparency requirement can lead to strange behaviors which make |
| 156 | + * it hard to reason about the code because an exception in the `collect { ... }` could be somehow "caught" |
| 157 | + * by an upstream flow, limiting the ability of local reasoning about the code. |
| 158 | + * |
| 159 | + * Flow machinery enforces exception transparency at runtime and throws [IllegalStateException] on any attempt to emit a value, |
| 160 | + * if an exception has been thrown on previous attempt. |
| 161 | + * |
| 162 | + * ### Reactive streams |
| 163 | + * |
| 164 | + * Flow is [Reactive Streams](http://www.reactive-streams.org/) compliant, you can safely interop it with |
| 165 | + * reactive streams using [Flow.asPublisher] and [Publisher.asFlow] from `kotlinx-coroutines-reactive` module. |
| 166 | + * |
| 167 | + * ### Not stable for inheritance |
| 168 | + * |
| 169 | + * **The `Flow` interface is not stable for inheritance in 3rd party libraries**, as new methods |
| 170 | + * might be added to this interface in the future, but is stable for use. |
| 171 | + * |
| 172 | + * Use the `flow { ... }` builder function to create an implementation, or extend [AbstractFlow]. |
| 173 | + * These implementations ensure that the context preservation property is not violated, and prevent most |
| 174 | + * of the developer mistakes related to concurrency, inconsistent flow dispatchers, and cancellation. |
| 175 | + */ |
| 176 | +public actual interface Flow<out T> { |
| 177 | + |
| 178 | + /** |
| 179 | + * Accepts the given [collector] and [emits][FlowCollector.emit] values into it. |
| 180 | + * |
| 181 | + * This method can be used along with SAM-conversion of [FlowCollector]: |
| 182 | + * ``` |
| 183 | + * myFlow.collect { value -> println("Collected $value") } |
| 184 | + * ``` |
| 185 | + * |
| 186 | + * ### Method inheritance |
| 187 | + * |
| 188 | + * To ensure the context preservation property, it is not recommended implementing this method directly. |
| 189 | + * Instead, [AbstractFlow] can be used as the base type to properly ensure flow's properties. |
| 190 | + * |
| 191 | + * All default flow implementations ensure context preservation and exception transparency properties on a best-effort basis |
| 192 | + * and throw [IllegalStateException] if a violation was detected. |
| 193 | + */ |
| 194 | + public actual suspend fun collect(collector: FlowCollector<T>) |
| 195 | +} |
0 commit comments