I would like to ask you why does it work?
Normally when I used collectLatest with flow my data wasn't collected on time and the return value was empty. I have to use async-await coroutines, but I have read it blocks main thread, so it is not efficient. I've made my research and find the solution using sharedflow.
Previously:
suspend fun getList: List<Items> {
CoroutineScope(Dispatchers.Main).launch {
async {
flow.collectLatest {
myItems = it
}
}.await()
}
return myItems
}
or without await-async and it returns emptyList
now:
suspend fun getList: List<Items> {
val sharedFlow = flow.conflate().shareIn(
coroutineScopeIO,
replay = 1,
started = SharingStarted.WhileSubscribed()
)
return sharedFlow.first()
}
conflate means:
Conflates flow emissions via conflated channel and runs collector in a separate coroutine. The effect of this is that emitter is never suspended due to a slow collector, but collector always gets the most recent value emitted.
I'm not sure I understand it clearly. When I conflate flow, I just create seperate coroutine to emit what will be inside my another function as in my example shareIn().first() and using this variablesharedFlow which is suspended so will give the same effect I made asnyc-await, but in that case I do not block main thread, but only my exact *parentCoroutine-or-suspendFunction?
SharingStarted.WhileSubscribed()
It just means to start emit when subcribed.
conflate() has nothing to do with why this is working. The separate coroutine it talks about is run under the hood and you don't need to think about it. It's just to make sure your flow never causes the upstream emitter to have to wait for a slow collector, and your collector skips values if they are coming faster than it can handle them. conflate() makes it safe to have a slow collector without a buffer.
In your first code block, you are launching a new coroutine in a new CoroutineScope, so it is not a child coroutine and will not be waited for before the function returns. (Incidentally, this new coroutine will only finish when the Flow completes, and most types of Flows never complete.)
In the second code block, you are calling first() on the Flow, which suspends and gets the next value emitted by the flow and then returns that value without waiting for the Flow to complete.
Some other notes:
You should never use async { /*...*/ }.await() where await() is called immediately on the Deferred, because it is just a more convoluted version of withContext(/*...*/) { /*...*/ }.
It's a code smell to create a CoroutineScope that you never assign to a property, because the point of creating a scope is so you can manage the scope, and you obviously aren't managing it if you have no reference to it to work with.
You said you are worried about blocking the main thread, but nothing in the code you showed looks suspicious of blocking the main thread. But it's possible your flow that you are basing this on has blocking code in it. By convention it shouldn't. If that flow blocks, you should use the flowOn(Dispatchers.IO) operator on it at the source so downstream users don't have to worry about it.
Although your code worked, it doesn't make sense to create a SharedFlow in a function and immediately collect from it. It's not being shared with anything! Your code could be simplified to this equivalent code:
suspend fun getList: List<Items> {
return flow.first()
}
Related
I'm a little confused.
I know that if a function wants to work with coroutines, it should be declared as suspend
For example:
private suspend fun doSomething() {
withContext(Dispatchers.IO) {
//do something
} }
And I also know that there is such a way to use coroutines without the function being suspend.
like:
private fun doSomething1() {
CoroutineScope(Dispatchers.IO).launch {
//do something
} }
What is the difference between the two functions?
When to use the first example and when to use the second example?
What is the difference between the two functions?
There are 2 major differences between the 2:
the usage is different: the suspend one "feels" synchronous, while the launch is explicitly asynchronous
the second function breaks structured concurrency, and shouldn't be written this way
Let me elaborate.
The suspend function appears synchronous from the usage perspective: when you call it, the next line of code is only executed when the function is done (like with any other regular function). This makes it easy to reason about. You can even assign the return value of a suspend function to a variable, and go on with your life as if the function wasn't suspend. That is, when you're in a suspend context already of course. When you're not, you have to start the "root" coroutine with an explicit coroutine builder (like launch, async or runBlocking).
When using launch, you're explicitly starting an asynchronous task, and thus the code after launch runs concurrently with what's inside the launch. So in turn, when calling doSomething1(), the code after it will run concurrently with whatever is in the launch inside. However, it is really not clear from the API's perspective that this function will launch a task that outlives it. This also goes with the fact that you shouldn't create "free" coroutine scopes like this. I'll elaborate below.
When to use the first example and when to use the second example?
Use suspend functions as much as possible to keep things simple. Most of the time, you don't need to start tasks that outlive the function call, so this is perfectly fine. You can still do some work concurrently inside your suspend function by using coroutineScope { ... } to launch some coroutines. This doesn't require an externally-provided scope, and all the computation will happen within the suspend function call from the caller's perspective, because coroutineScope {} will wait for the child coroutines to complete before it returns.
The function using launch as written here is very poorly behaved, you should never write things like this:
CoroutineScopes should not be created on the spot and left for dead. You should keep a handle on it and cancel it when appropriate
if you're already in the suspending world when calling this function, the existing coroutine context and jobs will be ignored
To avoid these problems, you can make the API explicit by making the CoroutineScope a receiver instead of creating one on the spot:
private fun CoroutineScope.doSomething1() {
launch(Dispatchers.IO) {
//do something
}
}
But only use this approach if the essence of the function is to start something that will keep going after the function returns.
The shortest answer is that suspend function is a block that can be executed in CoroutineScope. So it's not the first example vs the second example.
By combining those blocks you can start your own scope, and execute suspend functions using different contexts.
private suspend fun doSomething() {
withContext(Dispatchers.IO){
// task executed in io thread
}
}
private suspend fun doSomethingUI() {
withContext(Dispatchers.Main) {
// task executed in ui thread
}
}
private fun ioOperation() {
CoroutineScope(Dispatchers.IO).launch {
doSomething()
doSomethingUI()
}
}
Edit: This is just a basic sample made with simplicity in mind. It doesn't handle the proper lifecycle of the Coroutine Scope, and should not be directly used.
Why we should use suspend?
Are they used only to restrict a function not to be used outside coroutine scope or other suspend functions?
Are they used only to restrict a function not to be used outside co routine scope or other suspend functions?
No. The main purpose of a suspend function is to suspend a coroutine it is called in to eliminate callback hell. Consider the following code with a callback:
fun interface Callback {
fun call()
}
fun executeRequest(c: Callback) {
// ... create new Thread to execute some request and call callback after the request is executed
c.call()
}
On the caller side there will be:
executeRequest {
// ... do sth after request is competed.
}
Imagine you need to make another request after that one:
executeRequest {
// make another request
executeRequest {
// and another one
executeRequest {
// and another one ...
executeRequest {
}
}
}
}
That is a Callback Hell. To avoid it we can get rid of a Callback code and use only suspend functions:
// withContext(Dispatchers.IO) switches a coroutine's context to background thread
suspend fun executeRequest() = withContext(Dispatchers.IO) {
// just execute request, don't create another Thread, it is already in the background Thread
// if there is some result, return it
}
And on the caller side if there are a couple of requests, that should be executed one by one, we can call them without the Callback Hell by launching a coroutine and calling those request in the coroutine:
viewModelScope.launch {
executeRequest()
executeRequest()
executeRequest()
}
If you wonder what it does, I guess you will find the answer in this near-duplicate question's answer.
It basically allows to use the syntax of synchronous calls to call an asynchronous function.
If the question is "Why use async programming?", then there are probably plenty of resources explaining this on the internet. It usually allows to use threads more effectively and avoids using too many resources.
Now as to why you would want to use suspend to do that instead of callbacks, there is probably several reasons. Probably the biggest of them is to avoid the infamous "callback hell" (well known in JS): using actual callbacks creates nesting in the code. It makes the language harder to manipulate because you can't use local variables or loops as easily as with regular sequential code. With suspend functions, the code reads sequentially even though some asynchronous mechanisms are used behind the scenes to resume executing a piece of code at a later point.
Another big reason to use suspend (and more precisely the coroutines library in general) is structured concurrency. It allows to organize your asynchronous work so you don't leak anything.
Under the hood, suspend functions provide the extra functionality (involving a hidden Continuation object) that allow their code to be stopped and resumed later from where it left off, instead of the typical limitation that all function calls in a function are called in sequence and block the thread the entire time. The keyword is there to make the syntax very simple for doing a long-running task in the background.
Three different benefits of this:
No need for callbacks for long-running tasks ("callback hell")
Without suspend functions, you would have to use callbacks to prevent a long-running task from blocking the thread, and this leads to complicated-looking code that is not written in sequential order, especially when you need to do a number of long-running tasks in sequence and even more-so if you need to do them in parallel.
Structured concurrency
Built-in features for automatically cancelling long-running tasks. Guarantees about shared state being correct whenever accessed despite thread switching.
No need for state machines for lazy iterators
A limited set of suspend functions can be used in lazy iterator functions like the iterator { } and sequence { } builders. These are a very different kind of coroutine than the ones launched with CoroutineScopes, because they don't do anything with thread switching. The yield() suspend function in these builders' lambdas allows the code to be paused until the next item is requested in iteration. No thread is being blocked, but not because it's doing some task in the background. It instead enables code to be written sequentially and concisely without a complicated state machine.
I've looked at plenty of articles online but I'm still a bit confused as to what happens specifically, step by step, when suspend functions are suspended in coroutines.
I know that a suspend function, under the hood, is just a regular function with a Continuation parameter that allows it to resume, but my confusion is regarding where that suspend function or coroutine goes and where it comes back once resumed.
I've heard a few people saying "they don't necessarily come back to the same thread" and I don't get it, can someone please explain this to me step by step?
TLDR;
There is no guarantee, it may or may not,
it really depends that on the following points:
Is the dispatcher is multi-threaded?
Is there any dispatcher override in between?
LONG Answer
A coroutine has a CoroutineContext that specify how it behaves, where it run.
A CoroutineContext is mainly build up with four elements: Job, CoroutineName, CoroutineExceptionHandler and Dispatcher.
Its responsibility of the dispatcher to dispatch the coroutine. A dispatcher can be paused to stop coroutines to even run (this is useful in unit testing) mentioned here in the android conference talk, it may be a single-threaded dispatcher just like Dispatchers.Main, it has an event-loop like javascript has.
So, it really depends that on the following points:
Is the dispatcher is multi-threaded?
For example: This will run on single thread.
suspend fun main() {
val dispatcherScope = CoroutineScope(Executors.newSingleThreadExecutor().asCoroutineDispatcher())
val job = dispatcherScope.launch {
repeat(10) {
launch {
println("I'm working in thread ${Thread.currentThread().name}")
// every coroutine on same thread
}
}
}
job.join()
}
Run it here, other single threaded dispatchers: Dispatchers.Main
Is there any dispatcher override in between?
With the same context, if we override the dispatcher before launch, it will change the thread even if original context is based on single-threaded event-loop, each coroutine will run on different thread creating 10 different thread:
dispatcherScope.launch {
repeat(10) {
launch(Dispatchers.IO) {
println("I'm working in thread ${Thread.currentThread().name}")
// every coroutine on same thread
}
}
}
Run it here, other multi-threaded Dispatchers: Dispatchers.Default, Executor based dispatcher, Dispatchers.Unconfined (this launch coroutine in any free thread).
The short answer is: they execute somewhere and come back with the result to somewhere.
The long(er) explanation for the short answer:
"Somewhere" might be the same thread, it might be a different thread - it depends on the dispatcher and in many cases, the current state of the dispatcher. For instance, the contents of a SequenceScope will (by default) run on the same thread.
Another case where a suspend function might run on the same thread is if it's using the same dispatcher as the calling function.
Dispatchers can also share threads between them to save on thread creation (and just keep a count of the maximum number of parallel operations for their own tasks), so even switching between different dispatchers that each use thread pools may not result in using a different thread.
As far as people saying "they don't necessarily come back to the same thread," this is correct, for similar reasons. Keep in mind that your dispatcher may have many threads, and the one that you were using before you got suspended might be occupied with a different function right now, so the dispatcher will simply pick a different thread to run your code on.
When the coroutine suspends, the underlying Java method returns a special COROUTINE_SUSPENDED value. If the calling function is also suspendable, it also returns the object, and so the execution returns to the innermost plain, non-suspendable function. This function typically runs an event loop, where event handlers are calls to continuation.resume(). So now it is ready to take the next handler from the queue and resume another coroutine.
When you call continuation.resume(), the continuation itself knows about the dispatcher in charge of the coroutine and delegates to it. If the current thread isn't owned by that dispatcher, it dispatches an event to another event loop, the one served by the dispatcher's thread pool. This way the dispatcher controls the thread where the corutine resumes.
I have a function to make network calls. It can be called multiple times at the same time and each call runs concurrently.
getDataTask() // it is subscribed on the background thread and observed on the main thread
.subscribe(
{ result -> onResult(result) },
{ onError() }
)
.addToDisposables()
I am able to retrieve the data without any problems. onResult function updates the MutableLiveData field in the ViewModel as
private val _data = MutableLiveData<Data>()
val data: LiveData<Data> get() = _data
private fun onResult(result: Data) = _data.post(result)
And the field is set to be observed in the Fragment's onViewCreated method as
viewModel.data.observe(viewLifecycleOwner, Observer { data -> // DO SOMETHING })
When the back-to-back concurrent calls succeed and try to update the _data field, I noticed that the observer does not observe some of the results. For example, there are 4 concurrent calls start at the same time then they try to post their results and 1 or 2 of the results are not observed. How can I tackle that issue? Any help would be appreciated.
The problem as I believe occurs due to the nature of LiveData. LiveData being an Observable ViewHolder will simply emit only the last value that it processed.
Now I understand you are doing concurrent calls and there is nothing wrong with that. If you want to save execution time in networking then that is the way to go. However, if you want this concurrency to work with your LiveData instance, and you want your LiveData to emit each and every value it process as soon as a concurrent method successfully returns, then you might be using LiveData wrong.
What you are seeking is LiveData's Observable nature. You get the lifecycle aware part bundled with it for free, but then again its not your regular Observable, but a special one which only retains the last value.
In your case, where you want to process each and every value, you are better with using RxJava's Observable. You can make it lifecycle aware as well, by subscribing & unsubscribing in your activity or fragment. You have so many operators in RxJava that one of them will certainly help you, couple that I can think are switchMap and Map.
I went through the Kotlin Coroutine, I understood how it works but I have a confusion between Kotlin coroutine & Android Async.execute() & Async await. The Kotlin coroutine runs in the background and does not block on the UI thread but the same thing happens when we start android AsyncTask(with the methods doInBackground onPostExecute and onProgressUpdate overridden), it also does the computation in a background thread and publishes the result on the UI thread.
Async-await returns a Deffered object means the result will obviously going to be returned in future.
Can Anyone Explain what's the difference between these.
Let's try to break this down:
The Kotlin coroutine runs in the background
A coroutine CAN run in the background
does not block on the UI thread
Let's talk about what a coroutine is:
A coroutine can be thought of as the code that gets passed to one of the coroutine builder functions i.e. launch {}
By definition when a coroutine is launched, suspend functions within it do not block the corresponding Thread when they are reached; they "pause" the coroutine.
When the suspension point is reached, it is as if you were telling the code to "call you back later" when the result is available; a suspension point can be though of as a callback.
Let's look at an example:
fun main() {
val job = MainScope().launch {
doSomeWork()
}
suspend fun doSomeWork() {/*expensive work goes here*/}
}
When doSomeWork() is reached the code will suspend the coroutine, i.e. the suspend modifier is indicating to the coroutine framework that it can go do some other coroutine related work and then come back to this point when doSomeWork() is done.
Since this coroutine is launched using MainScope() it will be launched in the main Thread. That is why I said that coroutines CAN run in a background Thread but not always do so. In this case it does not, but it still does not block the UI Thread.
In the other hand, AsyncTask was (it is deprecated as of API 30) a mechanism that performed a tasks in a background Thread and posted the result back to the UI Thread
For the difference between CoroutineScope.async{} and CoroutineScope.launch{} we can look at the return values for each. As I showed in the example. launch{} returns a Job which is a representation of the lifecycle of the coroutine itself. Using the Job you can cancel() or join() the coroutine; you have control over its lifecycle. As you mention, async{} returns a Deffered<T> which is a representation of a future value. When await() is called on the Deffered<T> the coroutine is suspended until the result is ready to be consumed.