```mainScore = CoroutineScope(dispatchers.MAIN + j...
# coroutines
m
Copy code
mainScore = CoroutineScope(dispatchers.MAIN + job)
i have this
🧵 1
supervisor scope may solve
a
Supervisor scope just stops child exceptions from cancelling the parent/other children I believe
👍 2
z
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
i ommited the rest of the code, there's multiple parallel calls there
a
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
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
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
setUserAttrs() : Observable<T>
is what crashes?
m
yes
its retrofit stuff, for instance, if i disable internet
a
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
if i add an exception handler to the mainScope, it won't throw. its weird
a
Are you certain this is the code that is failing though?
👍 1
m
yes
a
What are you calling? Can you make a unit test for this? No component here needs to deal with the main thread yet
m
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
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
1s
z
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
yes, same result
wrapping it with
Copy code
withContext
and set the dispatcher there works
z
this should work, which sounds like what you did..
Copy code
async {
  withContext(<http://dispatchers.IO|dispatchers.IO>) { }
}
a
With context propagates the exception
m
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
yeah I want to understand this too
a
Can you try using global scope or dispatcher.default in your main score as opposed to unconfined?
m
yes
you right, it won't throw
a
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
nop. runBlocking same result with unconfined
a
That still doesn't explain your crash in your original code though since you didn't start with an unconfined scope
m
yes
u
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
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
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
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
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