commonMain.flow.Builders.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlinx-coroutines-core Show documentation
Show all versions of kotlinx-coroutines-core Show documentation
Coroutines support libraries for Kotlin
/*
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
@file:JvmMultifileClass
@file:JvmName("FlowKt")
package kotlinx.coroutines.flow
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import kotlinx.coroutines.flow.internal.*
import kotlin.coroutines.*
import kotlin.jvm.*
/**
* Creates a flow from the given suspendable [block].
*
* Example of usage:
* ```
* fun fibonacci(): Flow = flow {
* emit(1L)
* var f1 = 1L
* var f2 = 1L
* repeat(100) {
* var tmp = f1
* f1 = f2
* f2 += tmp
* emit(f1)
* }
* }
* ```
*
* `emit` should happen strictly in the dispatchers of the [block] in order to preserve the flow context.
* For example, the following code will result in an [IllegalStateException]:
* ```
* flow {
* emit(1) // Ok
* withContext(Dispatcher.IO) {
* emit(2) // Will fail with ISE
* }
* }
* ```
* If you want to switch the context of execution of a flow, use the [flowOn] operator.
*/
@FlowPreview
public fun flow(@BuilderInference block: suspend FlowCollector.() -> Unit): Flow {
return object : Flow {
override suspend fun collect(collector: FlowCollector) {
SafeCollector(collector, coroutineContext).block()
}
}
}
/**
* An analogue of the [flow] builder that does not check the context of execution of the resulting flow.
* Used in our own operators where we trust the context of invocations.
*/
@FlowPreview
@PublishedApi
internal fun unsafeFlow(@BuilderInference block: suspend FlowCollector.() -> Unit): Flow {
return object : Flow {
override suspend fun collect(collector: FlowCollector) {
collector.block()
}
}
}
/**
* Creates a flow that produces a single value from the given functional type.
*/
@FlowPreview
public fun (() -> T).asFlow(): Flow = unsafeFlow {
emit(invoke())
}
/**
* Creates a flow that produces a single value from the given functional type.
*/
@FlowPreview
public fun (suspend () -> T).asFlow(): Flow = unsafeFlow {
emit(invoke())
}
/**
* Creates a flow that produces values from the given iterable.
*/
@FlowPreview
public fun Iterable.asFlow(): Flow = unsafeFlow {
forEach { value ->
emit(value)
}
}
/**
* Creates a flow that produces values from the given iterable.
*/
@FlowPreview
public fun Iterator.asFlow(): Flow = unsafeFlow {
forEach { value ->
emit(value)
}
}
/**
* Creates a flow that produces values from the given sequence.
*/
@FlowPreview
public fun Sequence.asFlow(): Flow = unsafeFlow {
forEach { value ->
emit(value)
}
}
/**
* Creates a flow that produces values from the given array of elements.
*/
@FlowPreview
public fun flowOf(vararg elements: T): Flow = unsafeFlow {
for (element in elements) {
emit(element)
}
}
/**
* Returns an empty flow.
*/
@FlowPreview
public fun emptyFlow(): Flow = EmptyFlow
private object EmptyFlow : Flow {
override suspend fun collect(collector: FlowCollector) = Unit
}
/**
* Creates a flow that produces values from the given array.
*/
@FlowPreview
public fun Array.asFlow(): Flow = flow {
forEach { value ->
emit(value)
}
}
/**
* Creates flow that produces values from the given array.
*/
@FlowPreview
public fun IntArray.asFlow(): Flow = flow {
forEach { value ->
emit(value)
}
}
/**
* Creates flow that produces values from the given array.
*/
@FlowPreview
public fun LongArray.asFlow(): Flow = flow {
forEach { value ->
emit(value)
}
}
/**
* Creates flow that produces values from the given range.
*/
@FlowPreview
public fun IntRange.asFlow(): Flow = flow {
forEach { value ->
emit(value)
}
}
/**
* Creates flow that produces values from the given range.
*/
@FlowPreview
public fun LongRange.asFlow(): Flow = flow {
forEach { value ->
emit(value)
}
}
/**
* Creates an instance of the cold [Flow] with elements that are sent to a [SendChannel]
* that is provided to the builder's [block] of code. It allows elements to be
* produced by the code that is running in a different context, e.g. from a callback-based API.
*
* The resulting flow is _cold_, which means that [block] is called on each call of a terminal operator
* on the resulting flow. The [block] is not suspending deliberately, if you need suspending scope, [flow] builder
* should be used instead.
*
* To control backpressure, [bufferSize] is used and matches directly the `capacity` parameter of [Channel] factory.
* The provided channel can later be used by any external service to communicate with flow and its buffer determines
* backpressure buffer size or its behaviour (e.g. in case when [Channel.CONFLATED] was used).
*
* Example of usage:
* ```
* fun flowFrom(api: CallbackBasedApi): Flow = flowViaChannel { channel ->
* val callback = object : Callback { // implementation of some callback interface
* override fun onNextValue(value: T) {
* channel.offer(value) // Note: offer drops value when buffer is full
* }
* override fun onApiError(cause: Throwable) {
* channel.cancel("API Error", CancellationException(cause))
* }
* override fun onCompleted() = channel.close()
* }
* api.register(callback)
* channel.invokeOnClose {
* api.unregister(callback)
* }
* }
* ```
*/
@FlowPreview
public fun flowViaChannel(
bufferSize: Int = 16,
@BuilderInference block: CoroutineScope.(channel: SendChannel) -> Unit
): Flow {
return flow {
coroutineScope {
val channel = Channel(bufferSize)
launch {
block(channel)
}
channel.consumeEach { value ->
emit(value)
}
}
}
}