Antonio Labra
Antonio Labra

Reputation: 2010

Kotlin coroutines. Run code when API calls finish

I have a concern regarding coroutines. What I'd like to do is that a specific code is executed after several requests have been completed ... Before I used completionHandlers and work correctly, the problem is that there are more requests and I don't want to nest them, I think is not the correct practice ... So I don't know if you suggest anything for this exercise. I propose the following but I am not sure if it is the best:

  val job = viewLifecycleOwner.lifecycleScope.launch {
             // API calls
             Process1 (view)
             Process2 (view)
             Process3 (view)
             Process4 (view)
         }
         runBlocking {
             job.join ()
             // Continue normally
         }

Thanks for reading!

Ps. Each "Process" calls an API and responds as a completionHandler

Upvotes: 0

Views: 1587

Answers (1)

Benjamin Charais
Benjamin Charais

Reputation: 1368

Here is a simple example of 2 different methods of returning values from coroutines. You can not guarantee when a coroutine will return or finish, so you must either have some sort of callback function that does work with your api return data, or you can use the withContext method to work with it closer to a normal code flow. Either way, the coroutine code cant be joined back with synchronous code, and runBlocking is considered bad practice.

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class MyViews {
    private val apis = Apis()
    
    fun onViewCreate() {
        // Using example of launch -- cannot return value
        // This code will split off from the sync flow, and we cannot guarantee when any of it is ran, except the order it will be run in
        CoroutineScope(Dispatchers.IO).launch {
            // One method to elevate values out of a coroutine would be callbacks
                callApisWithCallbacks { data -> 
                    processApiData(data)
                }
        }
        
        // Internally this utilizes withContext to resolve a value that is ready to be used 
        callApisWithReturn()
        
        // Continue normal work flow here
    }
    
    fun processApiData(age: Int) {
        // Do work here
        println("Current users age: $age")
    }

    fun callApisWithReturn() {
        // Use launch to get into coroutine from sync code
        CoroutineScope(Dispatchers.Default).launch {
            // withContext will return a value and can be used as if inline
            val userAge = withContext(Dispatchers.IO) { apis.getUserAge() }
            processApiData(userAge)
        } 
        
        // Suspend function cant be called outside of coroutine
        // apis.getUserAge()
    }

    suspend fun callApisWithCallbacks(cb: (Int) -> Unit) {
        val age = apis.getUserAge()
        cb(age)
    }
}

class Apis {

    suspend fun getUserAge(): Int {
        // Simulate delay from API
        delay(1000)

        return 5
    }
}

Upvotes: 2

Related Questions