0

With Kotlin Coroutines I need to run two I/O work inside a try block at same time parallely without blocking each other.
And if any of the work fails, then it should be caught inside a single catch block.
Two operations are:
a) Continuously ping the server at a 10 second interval.
b) Make a network call with okhttp.

I need to cover these two operations inside a suspend function. PSB code:

suspend fun sendAllRequests() {
    try {
      pingServer()
    
      makeNetworkCall()
    
    } catch(exception: CustomException) {
      // Do some other work and throw exception to UI
      throw RuntimeException("Custom Error")    
    }
}
suspend fun pingServer() {
      job = GlobalScope.launch {
      while(true) {
         delay(10000) {
         if(isPingSuccess) {
           Log("Connection active")
         } else {
           Log("Connection Inactive")
           job.cancel()
           throw RuntimeException("Ping failed")
        }
      } 
    }
  }

suspend fun makeNetworkCall() {
    //This is a pseudo code just for explanation
    okhttp.newCall().await() 
    onFailure -> throw Exception
}

The challenge with above approach is that it, only initiates the logic present inside function pingServer().
i.e. The function makeNetworkCall() is never triggered.

And if I wrap the first function inside a new coroutine scope like below:
then both job works fine, but the exception thrown by pingServer() is never caught in catch block.

try {
   // With below logic, both functions are triggered 
   scope.launch { pingServer() } 

   makeNetworkCall()
} catch {
  // but pingServer() failure never reaches here
  // and makeNetworkCall() failure is properly captured here
}

How can I run the above two long running tasks parallely without blocking each other ?
How can I ensure that if any of them throws an exception, then it is caught inside the same catch block as shown above ?

1 Answer 1

1

Based on this article, an easy way to achieve what you are after is to not use launch but use async. Then you can run your makeNetworkCall() and after that await on your async deferred object catching the exception there

Something like this:

suspend fun sendAllRequests() {
    try {
      val deferred = GlobalScope.async { pingServer() }
    
      makeNetworkCall()
      deferred.await()

    } catch(exception: CustomException) {
      // Do some other work and throw exception to UI
      throw RuntimeException("Custom Error")    
    }
}

Please also note:

  1. The fact that you launch or async on another coroutine does not mean that code runs on background thread, you will need to launch your coroutine to another dispatcher for this (eg. Dispatchers.IO)
  2. Avoid using GlobalScope, you can easily create memory leaks doing so. Create your own scope or if you are on Android, try using the App Components scopes like viewModelScope or lifecycleScope
Sign up to request clarification or add additional context in comments.

1 Comment

It would usually be most appropriate to wrap this function in coroutineScope { } so you can directly use async as a child coroutine instead of some specific scope like you're suggesting in note 2. Then if the coroutine that called this function is cancelled, the child async coroutine will also be cancelled instead of leaking. True, if both coroutines happen to be called in a lifecycleScope or viewModelScope and the retirement of the scope is the only reason you'd ever cancel it, then this would be unnecessary, but it's less convoluted not to worry about that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.