2

I have a simple sequence of suspend functions (in Android Kotlin) which I want to run in sequence (once first is finished, then run second.)

I tried doing something which seamed reasonable like this:

class SomeService {

    init {

        GlobalScope.launch(Dispatchers.IO) {
            firstSuspendFunction()
            secondSuspendFunction()
        }
    }

}

However, only first one is run, second is never executed. Why is that so?

Edit: I tried to rule out some possible issues, and what seemed to worked was by completely emptying first function (empty body)!

The problem is that my firstFunction actually works, it has something like this:

private suspend fun firstSuspendFunction() {
    localProtoDataStore.preferences.collect {
        someDataList.addAll(it.preferenceData)
    }
}

Could it be that somehow launch never knowns if first suspend function has finished?

7
  • Are you sure that firstSuspendFunction() does not throw an Exception? This would cancel the coroutine and secondSuspendFunction() wouldn't be executed. Commented Dec 6, 2020 at 18:42
  • Thanks for the answer. I do not see any errors in the logs. How can I prove/debug this is the case? Commented Dec 6, 2020 at 18:47
  • I've added an edit, does it give more context? Commented Dec 6, 2020 at 18:57
  • 1
    Easiest way would be wrapping firstSuspendFunction in a try-catch. If secondSuspendFunction is executed now that's the cause of your problem. Commented Dec 6, 2020 at 19:51
  • 3
    Your code is correct. Something must be going wrong with firstSuspendFunction(). (It could also be running forever instead of failing -- e.g. if your flow doesn't terminate.) Commented Dec 6, 2020 at 20:39

2 Answers 2

0

I assume that localProtoDataStore.preferences returns a Flow and it seems infinite, because it listens to the preferences changes. Calling collect() on such Flow will suspend any function until the collection is finished (but it never finished because the Flow is infinite) or CoroutineScope, which executed a coroutine, is canceled.

So to execute the second function you have a couple of options. For example you can get only one value from the Flow:

private suspend fun firstSuspendFunction() {
    localProtoDataStore.preferences.firstOrNull {
        someDataList.addAll(it.preferenceData)
    }
}

Or you can get rid of using Flow in the firstSuspendFunction() and just retrieve a current data in preferences:

private suspend fun firstSuspendFunction() {
    val data = localProtoDataStore.preferences.get() // I don't know the exact name of the function, but the idea is clear
    someDataList.addAll(data)
}

Or you can just launch another coroutine to collect data from the Flow:

scope.launch(Dispatchers.IO) {
    launch {
        firstSuspendFunction()
    }
    secondSuspendFunction()
}
Sign up to request clarification or add additional context in comments.

Comments

0

Launch new Coroutine for each function call inside parent coroutine so that they can collect data from Flow individually without falling under suspension of parent coroutine. For example, in your case:

GlobalScope.launch(Dispatchers.IO) {
    launch { 
        firstSuspendFunction()
    }
    launch { 
        secondSuspendFunction()
    }
    ....
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.