-
Notifications
You must be signed in to change notification settings - Fork 23
Flow: Terminal operators
Devrath edited this page Jan 19, 2024
·
20 revisions
Terminal Operators |
---|
First |
Last |
Single |
ToList And ToSet |
LaunchIn |
Differences between Launch and LaunchIn |
Terminal operators are the operators that start the flow by connecting the flow builder, operators with the collector.
No flow is started/executed unless a terminal operator is used on a flow.
- You can notice that we have used
first
operator. - Also, even the second edition has not happened, so emission stops after the
first emission
. - The flow is canceled once the
first emission
is received. - If there are no elements then it would throw
java.util.NoSuchElementException:
To prevent this we can usefirstOrNull
instead of justfirst
@HiltViewModel
class TerminalOperatorsVm @Inject constructor(
@ApplicationContext private val context: Context,
) : ViewModel() {
companion object {
const val emissionDelay : Long = 100
}
private val terminalOperatorDemo = flow {
delay(emissionDelay)
println("Emitting first value")
emit(1)
delay(emissionDelay)
println("Emitting second value")
emit(2)
}
/** *********************** DEMO's *********************** **/
/**
* Terminal Operator: First
*/
fun demoFirst() {
viewModelScope.launch {
val result = terminalOperatorDemo.first()
println("Result:-> $result")
}
}
/** *********************** DEMO's *********************** **/
}
Emitting first value
Result:-> 1
- Here also observe that all the emissions are done from the flow but only the last emission is received in flow
@HiltViewModel
class TerminalOperatorsVm @Inject constructor(
@ApplicationContext private val context: Context,
) : ViewModel() {
companion object {
const val emissionDelay : Long = 100
}
private val terminalOperatorDemo = flow <Int>{
delay(emissionDelay)
println("Emitting first value")
emit(1)
delay(emissionDelay)
println("Emitting second value")
emit(2)
}
/** *********************** DEMO's *********************** **/
fun demoLast() {
viewModelScope.launch {
val result = terminalOperatorDemo.lastOrNull()
println("Result:-> $result")
}
}
/** *********************** DEMO's *********************** **/
}
Emitting first value
Emitting second value
Result:-> 2
- If there is more than one element in the flow
- If we use
single
it will returnIllegalArgumentException: More than one element
- If we use
singleOrNull
it will return null
- If we use
- If there is just one element in the flow, It will return that element.
viewModelScope.launch {
val result = terminalOperatorDemo.singleOrNull()
println("Result:-> $result")
}
- It provides us the ability to convert the flow into
List
andSet
.
fun toListAndToSet() {
viewModelScope.launch {
val resultList = terminalOperatorDemo.toList()
val resultSet = terminalOperatorDemo.toSet()
println("Result List:-> $resultList")
println("Result Set:-> $resultSet")
}
}
Result List:-> [1, 2]
Result Set:-> [1, 2]
-
LaunchIn
is not a suspended function but a regular function. - So it will not suspend the coroutine which it is called.
- Thus in the output observe that emissions are sent and receiving also happen before either of the co-routines to complete.
@HiltViewModel
class TerminalOperatorsVm @Inject constructor(
@ApplicationContext private val context: Context,
) : ViewModel() {
companion object {
const val emissionDelay : Long = 100
}
private val terminalOperatorDemo = flow <Int>{
delay(emissionDelay)
println("Emitting first value")
emit(1)
delay(emissionDelay)
println("Emitting second value")
emit(2)
}
fun launchIn() {
val scope = CoroutineScope(EmptyCoroutineContext)
viewModelScope.launch {
terminalOperatorDemo
.onEach { println("Result Collect <1>:-> $it") }
.launchIn(scope)
terminalOperatorDemo
.onEach { println("Result Collect <2>:-> $it") }
.launchIn(scope)
}
}
}
Emitting first value
Result Collect <1>:-> 1
Emitting first value
Result Collect <2>:-> 1
Emitting second value
Emitting second value
Result Collect <1>:-> 2
Result Collect <2>:-> 2
- If we use
scope.launch{}
Until the one flow inside the scope is completed the next flow is suspended and not executed, thus sequential. - In
launchIn(scope)
The coroutines are executed in parallel.
// <------------> Launch <-------------->
fun launch() {
val scope = CoroutineScope(EmptyCoroutineContext)
scope.launch {
// Collect -> Starting first collection
terminalOperatorDemo.collect{
println("Result Collect <1>:-> $it")
}
// <--- Until first collection is complete, collection is suspended --->
// Collect -> Starting second collection
terminalOperatorDemo.collect{
println("Result Collect <2>:-> $it")
}
}
// <------------> LaunchIn <------------>
fun launchIn() {
val scope = CoroutineScope(EmptyCoroutineContext)
viewModelScope.launch {
terminalOperatorDemo
.onEach { println("Result Collect <1>:-> $it") }
.launchIn(scope)
terminalOperatorDemo
.onEach { println("Result Collect <2>:-> $it") }
.launchIn(scope)
}
}