https://kotlinlang.org logo
Title
m

mingkangpan

03/06/2020, 9:01 AM
calling launch within launch is not structured concurrency anymore right?
val upperJob = launch {
 val jobFoo1 = launch {
      foo1()
   }
   val jobFoo2 = launch {
      foo2()  
   } 
}
upperJob.cancel() //won't cancel jobFoo1 and jobFoo2
right? I have to use
async
here
k

Kirill Prybylsky

03/06/2020, 9:06 AM
what behaviour you want to achieve?
m

mingkangpan

03/06/2020, 9:13 AM
I want to load 2 sets of data from 2 differnt APIs, if one fails, the other should not be cancled but I still want to be able to cancel both at once
k

Kirill Prybylsky

03/06/2020, 9:15 AM
easy, use like this
but, exception from first or second will be propagated up, so you have to handle it
m

mingkangpan

03/06/2020, 9:17 AM
how? where to put try catch? and whats the difference here to async?
k

Kirill Prybylsky

03/06/2020, 9:19 AM
Use try catch for the most upper coroutine or coroutineExceptionHandler. Difference between async/launch you can get in docs
m

mingkangpan

03/06/2020, 9:36 AM
okay launch and async will both excute the calls parallel?
k

Kirill Prybylsky

03/06/2020, 9:36 AM
yep
m

mingkangpan

03/06/2020, 9:38 AM
launch {
   try{
      supervisorScope {
        // first api
        launch { }
        
        //second api 
        launch { }
    }
   } cathc(e : Exception) {
   } 
}
launch {
   try{
      supervisorScope {
        // first api
        async { }
        
        //second api 
        async { }
    }
   } cathc(e : Exception) {
   } 
}
basically both will do what I want?
1
k

Kirill Prybylsky

03/06/2020, 9:41 AM
but with async you have to call await() to get the result
m

mingkangpan

03/06/2020, 9:41 AM
are you android developer?
k

Kirill Prybylsky

03/06/2020, 9:42 AM
android app developer
m

mingkangpan

03/06/2020, 9:43 AM
override fun onChanged(t: AccountInfo) {
        fetchDataAfterAccountInfo?.cancel()
        fetchDataAfterAccountInfo = viewModelScope.launch {
            try {
                supervisorScope {
                    launch {
                        val purchases = appService.fetchPurchasesWithPickupData().purchases
                        pickPurchasesLiveData.postValue(purchases)
                    }
                    launch {
                        val paymentPlan = appService.fetchPaymentPlan()
                        paymentPlanLiveData.postValue(paymentPlan.instalments)
                    }
                }
            } catch (e : Exception) {
                Timber.e(e, "loading data after accountInfo failed")
            }
        }
    }
so basically, that's what I want to to do, after a new accountInfo, I load 2 sets of data, which are independent from each other, but I want to be able to cancel both if there is a new accountInfo what do you think?
k

Kirill Prybylsky

03/06/2020, 9:44 AM
Here is complete code snippet.
m

mingkangpan

03/06/2020, 9:46 AM
if first api fails, what's the resultOfAll[0] then?
k

Kirill Prybylsky

03/06/2020, 9:48 AM
well, yes. move resultOfAll inside supervisor scope
or like this.
m

mingkangpan

03/06/2020, 9:49 AM
if first api fails, will second api executes uninfluenced?
k

Kirill Prybylsky

03/06/2020, 9:49 AM
yes, second will not be cancelled
m

mingkangpan

03/06/2020, 9:50 AM
lets maybe go back to my android code snippet, where should I put
liveData.posValue
?
k

Kirill Prybylsky

03/06/2020, 9:50 AM
let me check
code snippet that you sent is great, so you can use it 😉
m

mingkangpan

03/06/2020, 9:53 AM
so if both apis fails for example, I will get two exception, means timber will log 2 times? 🤯
k

Kirill Prybylsky

03/06/2020, 9:55 AM
yep, no exceptions will be lost
m

mingkangpan

03/06/2020, 9:56 AM
okay if it is really like this, then this is for sure too magical
🤯
k

Kirill Prybylsky

03/06/2020, 9:56 AM
lol
m

mingkangpan

03/06/2020, 10:13 AM
@Kirill Prybylsky
need to disappoint
fun main() = runBlocking {
    try {
        supervisorScope {
            launch { foo1() }
            launch { foo2() }
            delay(1000)
            println("Waited 1000ms")
        }
    } catch (e : Exception) {
        println(e)
    }
}

suspend fun foo1() {
    delay(300)
    throw RuntimeException("Something went wrong")
}

suspend fun foo2() : Int{
    delay(500)
    println("returning 5")
    return 5
}
Exception in thread "main" java.lang.RuntimeException: Something went wrong
	at com.minki.example.MainKt.foo1(Main.kt:20)
	at com.minki.example.MainKt$foo1$1.invokeSuspend(Main.kt)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:173)
	at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:111)
	at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:305)
	at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:315)
	at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:397)
	at kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask.run(EventLoop.common.kt:484)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:271)
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:79)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:54)
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:36)
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	at com.minki.example.MainKt.main(Main.kt:5)
	at com.minki.example.MainKt.main(Main.kt)
returning 5
Waited 1000ms
uncaught exception is thrown... so launch is a differnce to async
k

Kirill Prybylsky

03/06/2020, 10:16 AM
expected behaviour of coroutines, put try catch block inside of supervisorScope. i think we missed something.
m

mingkangpan

03/06/2020, 10:18 AM
still the same, launch and async have difference exception propagation...
k

Kirill Prybylsky

03/06/2020, 10:27 AM
s

streetsofboston

03/06/2020, 12:22 PM
@mingkangpan Maybe this can help you a bit: “Exceptional Exceptions for Coroutines made easy…?” by Anton Spaans https://link.medium.com/gKOAkJoMw4
👍 1