Suspend function inside flow kotlin - kotlin

So I have the below kotlin code with flows and suspend function.
suspend fun doWork(): Int {
// do some time intensive task
return if (task.isSuccessful){1}else{-1}
}
suspend fun sendTaskStatus(status:Int): Boolean {
// send this status data to the server via internet
return task.isSuccessful()
}
fun listenForCompletion() = flow <String> {
val isTaskCompleted = false
val taskStatus = doWork()
if (taskStaus == 1){
val isDataSent = sendTaskStatus()
if(isDataSent)
isTaskCompleted = true
}
emit(isTaskCompleted)
}
Now my question is if I collect listenForCompletion() function from a coroutine, what will be the sequence of execution of this code? If this isn't correct what would be the correct way of doing this assuming that flow and suspend functions are preserved?

Related

How to return a result of high-order function from inner function

I have a "high-order" function that have to return some value. Inside that "high-order" function there is an "inside" function which really is a producer of the return value of a "high-order" function.
It is simpler to show it with an example what I mean:
lifecycle.coroutineScope.launch {
val result = doWork()
Log.d("Tag", "some result: ${result.someString}")
}
private val service = SomeService()
suspend fun doWork(): DoWorkResult {
fun onSomeString(someString: String): DoWorkResult {
//some execution
val returnResultForDoWork = DoWorkResult(someString)
//How to return 'returnResultForDoWork' from fun doWork
return returnResultForDoWork
}
service.getString { someString ->
onSomeString(someString)
}
}
class SomeService() {
suspend fun getString(
onResult: (String) -> Unit
) {
delay(1000)
onResult("work is done")
}
}
data class DoWorkResult(val someString: String)
flow execution:
call service.getString
call onSomeString(someString) when a someString is return from service.getString
in onSomeString analyse/handle a someString and return (how?) a DoWorkResult(someString) from doWork
My question is how to return a result of an onSomeString function as a result of a doWork function?
Suspend functions don't need higher order callbacks like that. Really, it's an antipattern, because it restores back "callback hell" that coroutines solve. A proper version of your function would look like:
class SomeService() {
suspend fun getString(): String {
delay(1000)
return "work is done"
}
}
And then your calling function becomes:
suspend fun doWork(): DoWorkResult {
val serviceReturnValue = getString()
//some execution
val returnResultForDoWork = DoWorkResult(serviceReturnValue)
return returnResultForDoWork
}
But let's suppose your service function is not a suspend function, but rather it is asynchronous with a callback, and you don't have control over the source code to make it a suspend function instead.
class SomeService() {
fun getString(
onResult: (String) -> Unit
) {
val handler = Handler(Looper.myLooper())
thread {
Thread.sleep(1000) //simulate some work
handler.post { onResult("work is done") }
}
}
}
Then to be able to return the callback's inner value in a suspend function, you need to convert the asynchronous function into a suspending one. This can be done with suspendCoroutine or suspendCancellableCoroutine. There are many examples you can look up on this site or online, but here's a quick sample. You can write it as an extension function to work like an overloaded version of the asynchronous function.
suspend fun SomeService.getString(): String = suspendCoroutine { continuation ->
getString { continuation.resume(it) }
}
Now you can call this proper suspending version of the function just as in my second code block above.
Honestly, I am not quite sure if I really understand what you try to do but...
is this what you looking for?
private val service = SomeService()
data class DoWorkResult(val someString: String)
suspend fun doWork(): DoWorkResult {
fun onSomeString(someString: String): DoWorkResult {
//some execution
val returnResultForDoWork = DoWorkResult(someString)
//How to return 'returnResultForDoWork' from fun doWork
return returnResultForDoWork
}
return service.getString { someString ->
onSomeString(someString)
}
}
class SomeService {
suspend fun getString(onResult: (String) -> DoWorkResult): DoWorkResult {
delay(1000)
val myStringFromNetworkOrSomething = "work is done"
return onResult(myStringFromNetworkOrSomething)
}
}

Emit Flow via another suspend function in Kotlin

How can I have the flow collector below receive "hello"? The collector is calling myFunction1() which in turn calls myFunction2(). Both are suspend functions.
Currently nothing happens when I hit run and no flow is received. Am I missing something here?
CoroutineScope(IO).launch {
val flowCollector = repo.myFunction1()
.onEach { string ->
Log.d("flow received: ", string)
}
.launchIn(GlobalScope)
}
class Repo {
suspend fun myFunction1(): Flow<String> = flow {
/*some code*/
myFunction2()
}
suspend fun myFunction2(): Flow<String> = flow {
/*some code*/
emit("hello")
}
}
You can try to use emitAll function for your case:
fun myFunction1(): Flow<String> = flow {
/*some code*/
emitAll(myFunction2())
}
fun myFunction2(): Flow<String> = flow {
/*some code*/
emit("hello")
}
emitAll function collects all the values from the Flow, created by myFunction2() function and emits them to the collector.
And there is no reason to set a suspend modifier before each function, flow builder isn't suspend.
Unless you have a very specific reason the functions returning a Flow from your repo shouldn't be suspending (As the flow{} builder isn't suspending). Since the suspending operation is collecting (waiting for values to come out of it).
From the code you've provided you're looking for the flatMapLatest function. Docs here
class Repo {
fun function1() =
flow {
val value = doSomething()
emit(value)
}
.flatMapLatest { emittedValue -> function2() }
fun function2() = flow {...}
}

Is there a function that can write suspend function like Future&then in Dart?

sample code in dart:
void main() {
step1().then(step2).then(step3).then(print);
}
Future<String> step1() async {
return Future.value("setp1");
}
Future<int> step2(String input) async {
return Future.value(input.length);
}
Future<bool> step3(int input) async {
return Future.value(input > 3);
}
is there any way to write code in kotlin like this?
I use flow to write a simple code, but I won't find a way to simplify it
suspend fun step1(): String {
return "step1"
}
suspend fun step2(input: String): Int {
return input.length
}
suspend fun step3(input: Int): Boolean {
return input > 3
}
suspend fun execute() {
flowOf(step1())
.map { step2(it) }
.map { step3(it) }
.collect { print(it) }
}
I think you're saying you just want to run the suspend functions sequentially. If so, it doesn't look any different than non-suspending code.
suspend fun execute(){
val result1 = step1()
val result2 = step2(result1)
print(step3(result2))
}
If you really want to chain functions that are not extension functions, you can use run or let, although some might say this is less readable
suspend fun execute() = step1()
.run(::step2)
.run(::step3)
.run(::print)
If your functions are defined as extensions:
suspend fun String.step2() = length
suspend fun Int.step3() = this > 3
then you can chain directly:
suspend fun execute() = step1()
.step2()
.step3()
.run(::print)
kotlin coroutine does not adopt Promise like APIs (then/map/flatMap) because with suspend function, this can be done much easier
import kotlinx.coroutines.delay
import kotlinx.coroutines.yield
suspend fun main(args: Array<String>) {
val one = step1()
val two = step2(one)
val three = step3(two)
println(three)
}
suspend fun <T> resolve(value: T): T {
yield() // to simulate Future.resolve
return value
}
suspend fun step1() = resolve("100")
suspend fun step2(input: String) = resolve(input.length)
suspend fun step3(input: Int) = resolve(input > 3)
see discussions below
https://github.com/Kotlin/kotlinx.coroutines/issues/342
Using Coroutines in Kotlin
Synchronous run with three functions: here we perform three tasks in a coroutine and return the three results of the tasks in a Triple class.
suspend fun workWithThreeTimes(time1: Int, time2: Int, time3: Int): Triple<Result, Result, Result> {
return coroutineScope {
val result1 = manager.workForTime(time1) // work1
val result2 = manager.workForTime(time2) // work2 after work1
val result3 = manager.workForTime(time3) // work3 after work2
Triple(result1, result2, result3)
}
}
Parallel run with three functions: here we perform three works, in parallel, in a coroutine and return the three results of the tasks in a Triple class.
suspend fun workWithThreeTimesParallel(time1: Int, time2: Int, time3: Int): Triple<Result, Result, Result> {
return coroutineScope {
val work1 = async { manager.workForTime(time1) } // Async work1
val work2 = async { manager.workForTime(time2) } // Async work2 while work1 is working
val result3 = manager.workForTime(time3) // work3 while work1 and work2 are working
val result1 = work1.await() // non-blocking wait
val result2 = work2.await()// non-blocking wait
Triple(result1, result2, result3)
}
}

Pausing and resuming a sequence in Kotlin

The yield function in Kotlin states the following:
Yields a value to the Iterator being built and suspends until the next
value is requested.
In the following Kotlin code:
val sequence = sequence {
val start = 0
// yielding a single value
yield(start)
// yielding an iterable
yieldAll(1..5 step 2)
// yielding an infinite sequence
yieldAll(generateSequence(8) { it * 3 })
}
The yield statement is inside the sequence function. What I would like to do is have a Pause button that prevents further sequence items from being processed and a Resume button that when pressed allows the processing of the next item in the sequence. However if yield is inside of the sequence function, it isn't clear how a Pause/Resume flag can be used with this.
Sequences are just iterator suppliers, and iterators should be able to provide all elements without pauses:
for (elm in iterator) { }
// all elements must be created at this moment
// no additional elements allowed
Iterator can start sleeping during the next element providing. But, in this case, the caller thread (coroutine) will be blocked (suspended).
I assume you don't want to block threads, so I suggest creating a higher-order function that works like launch but returns Pauseable instead of Job:
fun CoroutineScope.launchPausable(block: suspend PauseableScope.() -> Unit): Pauseable =
PauseableCoroutine(this, block)
interface PauseableScope {
suspend fun pausePoint()
}
interface Pauseable {
fun pause()
fun resume()
val job: Job
}
class PauseableCoroutine(
scope: CoroutineScope,
block: suspend PauseableScope.() -> Unit
) : Pauseable, PauseableScope {
private var continuation: Continuation<Unit>? = null
private var isPaused = false
override val job = scope.launch { block() }
override fun pause() {
isPaused = true
}
override fun resume() {
syncIf({ isPaused }) {
isPaused = false
continuation?.resume(Unit)
continuation = null
}
}
override suspend fun pausePoint() {
suspendCoroutineUninterceptedOrReturn<Unit> { cont ->
syncIf({ isPaused }) {
continuation = cont
COROUTINE_SUSPENDED
} ?: Unit
}
}
}
Where syncIf is simple double-checked locking function:
inline fun <R> Any.syncIf(
crossinline condition: () -> Boolean,
crossinline block: () -> R
): R? {
if (condition())
synchronized(this) {
if (condition()) return block()
}
return null
}
Use case:
val pausable = GlobalScope.launchPausable {
items.forEach { item ->
doSomething(item)
// If it is paused coroutine will get suspended
pausePoint()
}
}
pausable.pause()
pausable.resume()
You can also put pausePoint() inside doSomething(item):
suspend fun PauseableScope.doSomething(item: Item) {
delay(100)
pausePoint()
delay(100)
}

how to break from lambda passed to recursive function when provided condition met

I am writing a custom loop dsl and I want it's usage to look like below
var counter1 = 0
var counter2 = 0
loop {
counter1 += 1
println(counter1)
stopIf(counter1 == 5) // loop should terminate here and not execute rest of the code if condition matches
counter2 += 2
println(counter2)
stopIf(counter2 == 8) // loop should terminate here and not execute rest of the code if condition matches
}
I have following code which does allows me to write stopIf any number of times and anywhere in the loop body but when condition matches it does not terminate immediately but executes rest of the loop body and then terminates.
#UseExperimental(ExperimentalTime::class)
open class Loop {
var stop = false
val loopInterval = 1.seconds
suspend fun loop(block: suspend () -> Unit): Unit = loop(loopInterval, block)
suspend fun loop(minimumInterval: Duration, block: suspend () -> Unit): Unit =
loopWithoutDelay { delayedResult(maxOf(minimumInterval, loopInterval), block) }
private suspend fun loopWithoutDelay(block: suspend () -> Unit) {
block()
if (stop) return else loopWithoutDelay(block)
}
suspend fun <T> delayedResult(minDelay: Duration, f: suspend () -> T): T = coroutineScope {
val futureValue = async { f() }
delay(minDelay.toJavaDuration())
futureValue.await()
}
fun stopIf(condition: Boolean) {
if (condition) {
stop = condition // once stop condition matches, then do not override it with following false condtions
}
}
}
#ExperimentalTime
suspend fun loop(block: suspend Loop.() -> Unit) =
Loop().run { loop { block(this) } }
I have tried to use return with label but it did not work. Is there any way I can achieve this?
It can be done for example with throwing a lightweight exception. You have to declare custom exception:
class LoopStopException : Throwable("Stop look", null, false, false) // lightweight throwable without the stack trace
and catch it in loopWithoutDelay:
private suspend fun loopWithoutDelay(block: suspend () -> Unit) {
try {
while (true) {
block()
}
} catch (e: LoopStopException) {
//do nothing
}
}
I didn't understand much about the function delayedResult, because none of the dsl's public functions return a result. However, I come up with an solution for cancelling the loop.
As far as I understood, we have to have a loop that doesn't block the current thread. Therefore, it must be run in a coroutine, but in order to be able to cancel the loop, the dsl must run its own coroutine. This inner coroutine is run using coroutineScope, so it suspends the parent coroutine until it's finished or cancelled.
#ExperimentalTime
class Loop {
private val loopInterval = 1.seconds
suspend fun loop(block: suspend () -> Unit) = loop(loopInterval, block)
suspend fun loop(minimumInterval: Duration, block: suspend () -> Unit):Job = coroutineScope {
launch {
while (true) {
block()
delay(minOf(minimumInterval, loopInterval).toLongMilliseconds())
}
}
}
suspend fun stopIf(condition: Boolean) = coroutineScope {
suspendCancellableCoroutine<Unit> {
if (condition) it.cancel() else it.resumeWith(Result.success(Unit))
}
}
}
#ExperimentalTime
suspend fun loop(block: suspend Loop.() -> Unit):Job {
return Loop().run {
this.loop {
block(this)
}
}
}