https://kotlinlang.org logo
#coroutines
Title
# coroutines
m

myanmarking

01/07/2019, 5:50 PM
Copy code
mainScore = CoroutineScope(dispatchers.MAIN + job)
i have this
🧵 1
supervisor scope may solve
a

Allan Wang

01/07/2019, 5:51 PM
Supervisor scope just stops child exceptions from cancelling the parent/other children I believe
👍 2
z

zak.taccardi

01/07/2019, 5:51 PM
You should be using
withContext
to switch a dispatcher, not
async{ }.await()
. Also in general you should avoid throwing exceptions and instead return values if you want predictable results and not weirdness like what you are experiencing now
m

myanmarking

01/07/2019, 5:52 PM
i ommited the rest of the code, there's multiple parallel calls there
a

Allan Wang

01/07/2019, 5:53 PM
I guess to address your question of throwing exceptions, if you can't change your network call, wrap the call with a try catch within async/withContext. Throwing an exception in async may change the behaviour in other jobs, even if it isn't thrown immediately
z

zak.taccardi

01/07/2019, 5:53 PM
where is your exception being thrown? it is thrown when you call
.await()
on your
Deferred
returned by
async
so your
try/catch
needs to wrap the
.await()
call on the
async
m

myanmarking

01/07/2019, 5:54 PM
i'll get a complete sample 1s
ok so in this example
Copy code
class SomeClass @Inject constructor(...
) {

    private val job = Job()
    private val mainScope = CoroutineScope(dispatchers.MAIN + job)

    private fun onSaveDataToServer() {
        val userId = sessionInfo.loggedInUserId

        
        mainScope.launch {

            try{
                val attr1 = async(<http://dispatchers.IO|dispatchers.IO>) { networkUser.setUserAttrs(...).awaitFirst() }
                val attr2 = async(<http://dispatchers.IO|dispatchers.IO>) { networkUser.setUserAttrs(...).awaitFirst() }
                val attr3 = async(<http://dispatchers.IO|dispatchers.IO>) { networkUser.setUserAttrs(...).awaitFirst() }

                awaitAll(attr1, attr2, attr3, prefs)

                ...
            }catch (e: Exception){
                Timber.e(e)
                ...
            }finally {
                ...
            }
        }
    }

    override fun onCleared() {
        mainScore.coroutineContext.cancelChildren()
    }
}
the app crash, try wont catch, for example, a HttpException
Copy code
setUserAttrs
is an observable, i can't change it, so i use the extension awaitFirst
z

zak.taccardi

01/07/2019, 6:02 PM
setUserAttrs() : Observable<T>
is what crashes?
m

myanmarking

01/07/2019, 6:02 PM
yes
its retrofit stuff, for instance, if i disable internet
a

Allan Wang

01/07/2019, 6:07 PM
Is there any log output whatsoever? Can you add an error handler to log the output? While I'm not too familiar with this, I don't see why the app is crashing here.
If you want, you can also make a helper function that will use try catch and log the error before wrapping it in async. I'm quite certain that would catch the error provided that the error is from that block
m

myanmarking

01/07/2019, 6:07 PM
if i add an exception handler to the mainScope, it won't throw. its weird
a

Allan Wang

01/07/2019, 6:07 PM
Are you certain this is the code that is failing though?
👍 1
m

myanmarking

01/07/2019, 6:07 PM
yes
a

Allan Wang

01/07/2019, 6:08 PM
What are you calling? Can you make a unit test for this? No component here needs to deal with the main thread yet
m

myanmarking

01/07/2019, 6:08 PM
yes, ill try
give me 5m
message has been deleted
i may be doing sth stupied. check please
this way it won't crash
Copy code
fun doWork() {
        mainScore.launch {
            try {
                withContext(Dispatchers.Unconfined) {
                    val attr1 = async { networkCall().awaitFirst() }
                    attr1.await()
                }
            } catch (e: Exception) { }
        }
    }
just why ?
z

zak.taccardi

01/07/2019, 6:22 PM
replace your:
Copy code
async(<http://dispatchers.IO|dispatchers.IO>) {
    networkUser.setUserAttrs(...).awaitFirst() 
}
with:
Copy code
async(<http://dispatchers.IO|dispatchers.IO>) {
    throw Exception("test test")
}
Do you have the same issue?
m

myanmarking

01/07/2019, 6:23 PM
1s
z

zak.taccardi

01/07/2019, 6:23 PM
the issue is probably:
async(<http://dispatchers.IO|dispatchers.IO>)
- you are changing the context
I wonder if
async(coroutineContext + <http://dispatchers.IO|dispatchers.IO>)
would have the same problem
m

myanmarking

01/07/2019, 6:24 PM
yes, same result
wrapping it with
Copy code
withContext
and set the dispatcher there works
z

zak.taccardi

01/07/2019, 6:24 PM
this should work, which sounds like what you did..
Copy code
async {
  withContext(<http://dispatchers.IO|dispatchers.IO>) { }
}
a

Allan Wang

01/07/2019, 6:24 PM
With context propagates the exception
m

myanmarking

01/07/2019, 6:25 PM
i did the other way, async inside withContext and it works
👍 1
i knew i could use withContext, but i don't understand why it works now but not before
there's no point in adding withContext if i cant get the problem =(
z

zak.taccardi

01/07/2019, 6:31 PM
yeah I want to understand this too
a

Allan Wang

01/07/2019, 6:32 PM
Can you try using global scope or dispatcher.default in your main score as opposed to unconfined?
m

myanmarking

01/07/2019, 6:33 PM
yes
you right, it won't throw
a

Allan Wang

01/07/2019, 6:36 PM
I also wonder if your test is finishing too early since you aren't blocking the request in your test. I try this myself in a few hours when I'm home if this isn't resolved by then
m

myanmarking

01/07/2019, 6:37 PM
nop. runBlocking same result with unconfined
a

Allan Wang

01/07/2019, 6:41 PM
That still doesn't explain your crash in your original code though since you didn't start with an unconfined scope
m

myanmarking

01/07/2019, 6:42 PM
yes
u

uli

01/08/2019, 6:51 AM
Exceptions in async code will no longer be rethrown in await since structured concurrency arrived because your scope will be cancelled even before await is called. Try wrapping all your code into a new scope with a try catch around the scope
a

Allan Wang

01/08/2019, 5:52 PM
Can you elaborate on that? Why the need of a new scope? And why does this still crash? https://kotlinlang.slack.com/archives/C1CFAFJSK/p1546883785264500?thread_ts=1546883418.261600&amp;cid=C1CFAFJSK The unit test is prematurely cancelled but that code seems like it's supposed to work (assuming it was tested from the main platform)
u

uli

01/08/2019, 8:27 PM
Take a look at issue 763 "Async builder and cancellation in structured concurrency" (sorry my phone won't paste the link). An exception in an async coroutine will cancel it's parent. The idea of creating an outer scope was to catch that cancellation exception. I have not tried that though
a

Allan Wang

01/08/2019, 8:31 PM
That’s why I suggested try catching within the async block above, but cancelling the parent shouldn’t result in an exception being thrown right? Apparently the entire app stopped, and I thought
launch
was only supposed to log exceptions by default
m

myanmarking

01/09/2019, 10:26 AM
yes, it seems the proper way of 'manually' handling exceptions in async block is to wrap in into a withContext scope, then the try would work
10 Views