1

Currently, I am making API calls to 2 to 3 different APIs, where the second API Call relies on data from the first. However, the compiler calls the 2nd function even before the first function is completed, causing an error. How can I call the 2nd function only after the first is done? Thank you

/**
     *  Function to get Bus Timings depending on Bus Service No. or Bus Stop Code
     */
    fun determineUserQuery(userInput: String) {
        // Determine if User Provided a Bus Service No. or Bus Stop Code
        val userInputResult = determineBusServiceorStop(userInput)
        viewModelScope.launch {
            if (userInputResult.busServiceBool) {
                busServiceBoolUiState = true
                coroutineScope {
                    // Provided Bus Service, Need get Route first
                    getBusRoutes(targetBusService = userInputResult.busServiceNo)
                }
                delay(2000)
                // Get the Bus Timing for Each Route
                Log.d("debug2", "String ${_busRouteUiState.value.busRouteArray}")
                getMultipleBusTimings(busRoutes = _busRouteUiState.value.busRouteArray)
            }
            else {
                // Provided Bus Stop Code
                coroutineScope {
                    launch {
                        getBusStopNames(targetBusStopCode = userInputResult.busStopCode?.toInt())
                    }
                    launch {
                        getBusTimings(userInput = userInputResult.busStopCode)
                    }
                }
            }


        }
    }
  • 2
    put them inside of the same launch block instead of creating two separate ones? – Pawel Dec 29 '22 at 11:15
  • Yup, just `coroutineScope.launch { ... }` – ArieDov Dec 29 '22 at 11:55
  • The main/only reason to use `launch()` is to execute the code concurrently, so if you want your code to be sequential then simply don't use it. You can pretty much remove all `launch()` except the first one and also remove all `coroutineScope()` and I believe the code will do what you need. – broot Dec 29 '22 at 14:07
  • @Pawel I see, but if it doesn't work for me, do uk what other error that I could have made that caused it? – Michelin_Boi Dec 29 '22 at 14:09
  • @broot I realised that as well but I have to use a coroutine scope to call my particular function, which I used viewmodelscope.launch {} to do. but it is not really working out, which is why I tried to create more child coroutines to try and fix it – Michelin_Boi Dec 29 '22 at 14:11
  • To provide more context, currently the only way I managed to force this in a coroutine is to use delay() – Michelin_Boi Dec 29 '22 at 14:13
  • 1
    What's inside `getBus*()` functions? I often observe a (anti)pattern where most functions start with `viewModelScope.launch()`. If this is what you do in these functions, then it is not really possible to properly wait for them to finish. Instead, turn these functions into suspend functions and do not use `viewModelScope.launch()` in them. That applies to `determineUserQuery()` as well. Write most of your code as traditional sequential code (only suspend). Use `viewModelScope.launch()` only in the top-most function where you e.g. handle an event and you need to start a coroutine. – broot Dec 29 '22 at 15:35
  • Have a look [here](https://stackoverflow.com/questions/56480520/kotlin-coroutines-sequential-execution/63494023#63494023) and [here](https://stackoverflow.com/questions/57330766/why-does-my-function-that-calls-an-api-or-launches-a-coroutine-return-an-empty-o/70178210#70178210) - also, as others pointed out, way too many unnecessary `launch` calls... – Tyler V Dec 31 '22 at 06:04
  • @broot THANK YOU SO MUCH it worked!!! it appears my understanding of coroutines is severely limited. Ill read up more!! – Michelin_Boi Jan 02 '23 at 02:13
  • @TylerV Thank you so much for the links!! Looking back after a few days, I really don't know what I was doing – Michelin_Boi Jan 02 '23 at 02:14

1 Answers1

0

There is a method join() in CoroutineScope's Launcher, that is a completion listener.

CoroutineScope(Dispatchers.Main).launch {
    CoroutineScope(Dispatchers.IO).launch {
        // Your 1st Task
        ...
    }.join()
    CoroutineScope(Dispatchers.IO).launch {
        // Your 2nd Task
        ...
    }.join()
    CoroutineScope(Dispatchers.IO).launch {
       // Your 3rd Task
        ...
    }.join()
}

There's another function async that also returns result wherever the function it gets call.. await()

CoroutineScope(Dispatchers.Main).launch {
    // Your 1st Task
    val result1 = CoroutineScope(Dispatchers.IO).async {
        val v1 = "Hello!"
        v1
    }
    // Your 2nd Task
    val result2 = CoroutineScope(Dispatchers.IO).async {
        val v2 = result1.await() + " Android"
        v2
    }
    // Your 3rd Task
    val result3 = CoroutineScope(Dispatchers.IO).async {
        val v3 = result2.await() + " Developer"
    }

    // execute all be calling following
    result3.await()
}
Sohaib Ahmed
  • 1,990
  • 1
  • 5
  • 23
  • I see!!! Is it necessary to include Dispatchers.IO in the first method? I tried using async but Im not sure where to append it, can I just append it to any coroutine scopes? Thank you – Michelin_Boi Dec 29 '22 at 14:14
  • `Dispatchers.IO` will do the process in background. Yes, you can also do in anyother scope.. All it needs is a CoroutineScope. – Sohaib Ahmed Dec 29 '22 at 15:47