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 use firstOrNull
instead of just first
@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 return IllegalArgumentException: More than one element
If we use singleOrNull
it will return null
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
and Set
.
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
Differences between Launch and LaunchIn
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)
}
}