Dimitri Fedorov
07/08/2019, 7:33 PMelement.call() {
service.run() {
element2.call() { ... }
}
}
there is 2 requirements:
1. every element or service is invoking callback on proper thread already
2. every call shouldn’t block its own thread, i.e. each callback should exit after invoking next element/service (i can’t use suspendCoroutine
because of that)
what is proper coroutine way to do that?Paul Woitaschek
07/09/2019, 8:01 AMflow { emit(mySuspendingFunction()) }
?Derek Berner
07/09/2019, 1:06 PMwithContext
to launch
a bunch of `Job`s in that context, does that mean I don't have to explicitly join
all those jobs at the end of the block?Paul Woitaschek
07/09/2019, 2:49 PMstreetsofboston
07/09/2019, 3:33 PMSupervisorJob
.
To me it seems the code has a bug in how exceptions propagate (or not) to the parent of a Coroutine run within a SupervisorJob. I logged the issue here:
https://github.com/Kotlin/kotlinx.coroutines/issues/1317
Can someone shed a light on how this is supposed to work? I’m a bit baffled 🙂
Thanks!bj0
07/09/2019, 11:45 PMFlow
of data that has separators in it, is there an easy way to split it into a flow of separated chunks? ie: "@some data@more data@
-> [some data, more data]
pablisco
07/10/2019, 8:13 AMkoufa
07/10/2019, 9:54 PMchannelFlow
and callbackFlow
?eygraber
07/11/2019, 2:45 AMNon-applicable call for builder inferenceand it occurs on the line that starts with
channel.send
override val animationProgressChannel: ReceiveChannel<Float> = resumeScope.produce {
lottieAnimation.addAnimatorUpdateListener { animation ->
resumeScope.launch {
channel.send(animation.animatedValue as Float) // Non-applicable call for builder inference (channel is highlighted)
}
}
}
If I change it to resumeScope.produce<Float>
the warning goes away, but the IDE suggests that I "remove the explicit type arguments". Anyone know what's up with that?Cyrille QUÉMIN
07/11/2019, 11:18 AMAntimonit
07/11/2019, 11:30 AMasync
coroutines that both return the same type. I want to return only the value of the coroutine that finishes first and discard the value of the slower one. I have found that select
takes care of selecting and returning value the faster coroutine but the other one is not cancelled and coroutineScope waits until the other one returns too. What would be the best approach?koufa
07/11/2019, 12:17 PMflowOf("one string", "second string", "third string")
.debounce(300)
.map { it.trim() }
.filter { it.length > 2 }
.flowOn(Dispatchers.Default)
.distinctUntilChanged()
.switchMap {
// Simulate server call
delay(2000)
flow {
// Just return search term for the moment
emit(it)
}
}
.flowOn(<http://Dispatchers.IO|Dispatchers.IO>)
.collect {
_viewState.value = it
}
Now I want to define a unit test for that but I am not sure how to do this for the above flow. Do I need to extract the flow except from the collector
at the end to a function and then add a test collector
and verify the values I would expect to get there? Or is there any other idiomatic way?Wilson Castiblanco
07/11/2019, 3:21 PMalex.hart
07/11/2019, 4:36 PMcache
or replay
would do from Rx)rocketraman
07/11/2019, 5:04 PMrunBlocking
remain parked, even though the underlying coroutines are no longer running?sam
07/11/2019, 8:01 PMflow.grouped(10)
// should give me List<T> now for each element, containing 10 elements except for the last list`Daniel
07/12/2019, 7:07 PMclass Database {
fun prefillDatabase() {
// launch a non blocking job here that puts some data in the database
// image this is done on application start
}
suspend fun getEntities() {
// launch a blocking coroutine to get all entities of the database but only AFTER the job in prefillDatabase() completed
// imagine this is called later in the application on button click.
}
}
Is this a case for .join()? The job started in prefillDatabase() could already be well completed before getEntities() is calledKulwinder Singh
07/13/2019, 11:49 AMAdriano Celentano
07/13/2019, 1:29 PMJag
07/14/2019, 4:55 AMrunBlockingTest
but it seems to eagerly execute despite invoking pauseDispatcher()
I tried out the sample provided on the kotlinx-coroutines-test documentation but still hitting similar problems. I’m wondering if there’s just something I’m overlooking here?
Here’s what I’m running from the documentation sample:
@Test
fun testFooWithPauseDispatcher() = runBlockingTest {
pauseDispatcher {
foo() // <-- this is still eagerly executed
// the coroutine started by foo has not run yet
// runCurrent() // the coroutine started by foo advances to delay(1_000)
// the coroutine started by foo has called println(1), and is suspended on delay(1_000)
// advanceTimeBy(1_000) // progress time, this will cause the delay to resume
// the coroutine started by foo has called println(2) and has completed here
}
}
fun CoroutineScope.foo() {
launch {
println(1) // executes after runCurrent() is called
delay(1_000) // suspends until time is advanced by at least 1_000
println(2) // executes after advanceTimeBy(1_000)
}
}
farzad
07/14/2019, 11:16 AMimport kotlinx.coroutines.*
fun main(args: Array<String>) {
runBlocking {
withTimeout(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}
}
}
then I'll get this output:
I'm sleeping 0 ...
I'm sleeping 1 ...
I'm sleeping 2 ...
Exception in thread "main" kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1300 ms
at kotlinx.coroutines.TimeoutKt.TimeoutCancellationException (Timeout.kt:126)
at kotlinx.coroutines.TimeoutCoroutine.run (Timeout.kt:92)
at kotlinx.coroutines.EventLoopImplBase$DelayedRunnableTask.run (EventLoop.kt:307)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent (EventLoop.kt:116)
at kotlinx.coroutines.DefaultExecutor.run (DefaultExecutor.kt:68)
at java.lang.Thread.run (Thread.java:745)
But if I run this one:
import kotlinx.coroutines.*
fun main(args: Array<String>) {
runBlocking {
launch{
withTimeout(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}
}
}
}
I'll get another output:
I'm sleeping 0 ...
I'm sleeping 1 ...
I'm sleeping 2 ...
why the second one doesnot throw exception?Dico
07/14/2019, 11:20 AMrunBlocking
In my program I have a top level coroutine dispatcher that uses runBlocking
as an event loop for the main thread.
In the application code, it uses runBlocking
somewhere else for something more trivial.
It seems that the dispatcher for the 2 contexts are sharing some state. It runs a few main loop frames when the second runBlocking
is called.
Moreover, the stacktrace in intellij, which says async stack trace, doesn't indicate any difference when the main loop is executed in this way.farzad
07/14/2019, 12:32 PMthana
07/14/2019, 1:41 PMsuspend
or just call a coroutine from within that method?Alexjok
07/14/2019, 8:27 PMthana
07/15/2019, 12:06 PM-javaagent:kotlinx-coroutines-debug-1.2.2.jar
but then i got java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics
. any idea what i am doing wrong?groostav
07/15/2019, 8:54 PMDominaezzz
07/16/2019, 9:02 AMContinuation
to a kotlin suspend function? Isn't it pretty much the same as using callbacks?tseisel
07/16/2019, 12:50 PMFlow
, I am somewhat confused about when I should use Flow
VS `Channel`s and `actor`s.
I know that Flow
represents a cold stream of values, but that stream can also be generated from a hot source, making it a deferrable hot stream.
Do you have some specific use cases where you'd use `Channel`s over Flow
, and why ? It seems to me that Flow
can handle most use-cases.ursus
07/16/2019, 1:04 PMursus
07/16/2019, 1:04 PMstreetsofboston
07/16/2019, 1:09 PMursus
07/16/2019, 1:10 PMtseisel
07/16/2019, 1:23 PMursus
07/16/2019, 1:24 PMstreetsofboston
07/16/2019, 1:31 PMCoroutineScope(...)
) and switch to it for running and caching your request.class MyViewModel : ViewModel() {
...
viewModelScope.launch {
...
val result = service.getDataFromNetwork(input)
liveData.value = result.toUiResult()
}
...
}
class ServiceImpl : Service {
private val scope = CoroutineScope(SupervisorJob() + <http://Dispatchers.IO|Dispatchers.IO>)
override suspend fun getDataFromNetwork(input: String): NetworkResult = coroutineScope {
scope.async {
val result = ... get result from network...
addResultToCache(result)
result
}.await()
}
}
ursus
07/16/2019, 1:52 PMstreetsofboston
07/16/2019, 1:52 PMliveData.value = result.toUiResult()
will never be called, because viewModelScope
was cancelled. However, the scope
in ServiceImpl
is not cancelled. The async
it launches runs until its completion.ursus
07/16/2019, 1:52 PMstreetsofboston
07/16/2019, 1:54 PMServiceImpl
and let ServiceImpl
manage wether to get the data from the network or wait if the network request is still going or getting it from the cache/dbasync
returns a Deferrable<T>
, which is a sub-class for a Job
.ursus
07/16/2019, 1:55 PMtseisel
07/16/2019, 1:56 PMWorkManager
: you schedule the task in the ViewModel
, but it is run in the scope of the Worker
. Therefore, the task is guaranteed to run to completion even if the ViewModel
is cleared.ursus
07/16/2019, 1:57 PMstreetsofboston
07/16/2019, 1:58 PMasync
calls with the request's input as the key. You can call await()
on a Deferred
multiple times. It will only run the async
once, the other times it will return the already obtained result.ursus
07/16/2019, 1:58 PMstreetsofboston
07/16/2019, 2:04 PMclass ServiceImpl : Service {
private val scope = CoroutineScope(SupervisorJob() + <http://Dispatchers.IO|Dispatchers.IO>)
private val cachedResults = mutableMapOf<Any, Deferred<*>>()
override suspend fun getDataFromNetwork(input: String): NetworkResult = coroutineScope {
var deferredResult = cachedResult[input] as Deferred<NetworkResult>
if (deferredResult == null) {
deferredResult = scope.async {
val result = ... get result from network using 'input'...
result
}
cachedResults[input] = deferredResult
}
deferredResult.await()
}
}
And await()
will throw an Exception if teh result was an exceptioncachedResults
.
Also, you'd need a way to clean up the cache when necessary.ursus
07/16/2019, 2:06 PMstreetsofboston
07/16/2019, 2:08 PMviewModelScope
launches its thing, is wherever you need it. I don't know where that would be, depends on your use-case. It could be in the init { ... }
block of your ViewModel, or on a button-click when your Fragment/Activity calls to a method on your ViewModel, etc.ursus
07/16/2019, 2:09 PMViewModel {
val liveData
fun syncButtonClicked() {
viewModelScope {
val result = service.getDataFromNetwork()
withContext(UI) {
liveData.set(result)
}
}
}
}
ViewModel {
val liveData
init {
viewModelScope {
val result = service.getDataFromNetwork(SYNC_KEY)
withContext(UI) {
liveData.set(result)
}
}
}
fun syncButtonClicked() {
viewModelScope {
val result = service.getDataFromNetwork(SYNC_KEY)
withContext(UI) {
liveData.set(result)
}
}
}
}
streetsofboston
07/16/2019, 2:11 PMursus
07/16/2019, 2:12 PMstreetsofboston
07/16/2019, 2:14 PMwithContext(Dispatchers.Main)
. Also, I don't see any call to launch
and such in your code....
Note that your init
block issues the request, even if the user has not pushed the button before.... you'll need another method on Service
(and ServiceImpl
) that queries if a cached Deferred<T> exists or not and if so, only then waits for it.ursus
07/16/2019, 2:19 PMstreetsofboston
07/16/2019, 2:27 PMService
, called something like queryDataFromNetwork(...)
that just awaits a result if the cachedResults
has a Deferred entry for the given input and returns immediatly null
if has no such entry.
Not sure what you mean with 'composability'. If you mean make it functional by composing lambdas/functions, I would do that later when stuff works. :-)
If you need status updates, like 'idle', 'progress', 'loading', ..., 'result', then a channel is better suited.ursus
07/16/2019, 2:27 PMstreetsofboston
07/16/2019, 2:27 PMursus
07/16/2019, 2:29 PMstreetsofboston
07/16/2019, 2:30 PMlaunc
or async
)ursus
07/16/2019, 2:33 PMViewModel {
val liveData
init {
viewModelScope {
service.syncStatusChannel.receive {
withContext(UI) {
liveData.set(result)
}
}
}
}
fun syncButtonClicked() {
service.fetchDataFromNetwork()
}
}
class ServiceImpl : Service {
private val _syncStatusChannel = MutableChannel
syncStatusChannel : Channel
get() = _syncStatusChannel
private val scope = CoroutineScope(SupervisorJob() + <http://Dispatchers.IO|Dispatchers.IO>)
override fun fetchDataFromNetwork(input: String): Unit = coroutineScope {
_syncStatusChannel.send(IN_PROGRESS)
val result = ... get result from network...
addResultToCache(result)
_syncStatusChannel.send(IDLE)
}
}
streetsofboston
07/16/2019, 2:38 PMreceive
would be consumeEach
. And not viewModeScope { ... }
, but viewModelScope.launch { ... }
instead.withContext(UI)
there either, since viewModelScope
already uses the Main-UI dispatcher.ursus
07/16/2019, 2:39 PMstreetsofboston
07/16/2019, 2:42 PMFlow
instead of a Channel
ursus
07/16/2019, 2:46 PMstreetsofboston
07/16/2019, 2:52 PMsuspend
fun to get the actual data in a Coroutine `launch`ed by your ViewModel and that code will then update a LiveData
property as well that is tied to a waiting/loading-spinner.zhuinden
07/16/2019, 4:07 PMGlobalScope.launch {
inside a singleton instead of from ViewModel directly? Then it won't have a reference to your ViewModel and won't be able to leak. You could use something like an event bus (channels?!) to communicate back.ursus
07/16/2019, 4:08 PM