最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

android - Trigger a coroutine when the other is finished - Stack Overflow

programmeradmin0浏览0评论

I do have an app where I have to call an endpoint to pull a list of product and another endpoint to pull the list of product type.

The second endpoint only return the list of type but I need to add the type All and add the total number of products`

So it looks like: -> Endpoint call for list of products: return a json with all the products -> Endpoint call for list of types: return all types.

When all types are received I add the type all at the beginning of the list and count the number of products. So I need to have the first endpoint call done prior to calling the other one.

I did this way for the init:


   private var listOfProducts: List<Product> = emptyList()
   private var listOfTypes: List<Type> = emptyList()
   private var selectedType: String = ""

   init {
      fetchProducts()
      fetchTypes()
   }

and the fetch place is call:

   fun fetchProducts() {
      viewModelScope.launch {
         API_CALL {
            listOfProducts = response
         }
      }
   }
   
   fun fetchTypes() {
      viewModelScope.launch {
         API_CALL {
            nb_films = listOfProducts.count()
            listOfTypes = Type("all", nb_films) + response
         }
      }
   }

So the code is not perfect, but it's just to sum up the issue.

I need to use information from the fetchProducts into the fetchTypes. In some cases, it fails because of the latency to get the data.

Any idea how to block the trigger of the fetchTypes or either making the 2 calls but waiting one to be ended before processing the data

I do have an app where I have to call an endpoint to pull a list of product and another endpoint to pull the list of product type.

The second endpoint only return the list of type but I need to add the type All and add the total number of products`

So it looks like: -> Endpoint call for list of products: return a json with all the products -> Endpoint call for list of types: return all types.

When all types are received I add the type all at the beginning of the list and count the number of products. So I need to have the first endpoint call done prior to calling the other one.

I did this way for the init:


   private var listOfProducts: List<Product> = emptyList()
   private var listOfTypes: List<Type> = emptyList()
   private var selectedType: String = ""

   init {
      fetchProducts()
      fetchTypes()
   }

and the fetch place is call:

   fun fetchProducts() {
      viewModelScope.launch {
         API_CALL {
            listOfProducts = response
         }
      }
   }
   
   fun fetchTypes() {
      viewModelScope.launch {
         API_CALL {
            nb_films = listOfProducts.count()
            listOfTypes = Type("all", nb_films) + response
         }
      }
   }

So the code is not perfect, but it's just to sum up the issue.

I need to use information from the fetchProducts into the fetchTypes. In some cases, it fails because of the latency to get the data.

Any idea how to block the trigger of the fetchTypes or either making the 2 calls but waiting one to be ended before processing the data

Share Improve this question asked Feb 2 at 20:03 SebSeb 3,2255 gold badges39 silver badges86 bronze badges 3
  • 1 You basically shoot yourself in foot by launch coroutines like this. Coroutines are sequential by default, so we don't need to do anything magic to run one operation after another. If both functions are suspend functions, solution would be as simple as: fetchTypes(fetchProducts()). With your current code, there is no trivial and reliable solution to the problem. – broot Commented Feb 2 at 21:41
  • Actually, you could potentially call fetchTypes inside the fetchProducts, after the listOfProducts = line. Or to keep the code less tightly coupled, receive a callback by the fetchTypes and do the above. But this is not really a "coroutine way". These functions should suspend and don't launch anything. – broot Commented Feb 2 at 21:50
  • viewModelScope.launch { fetchProducts() .flatMapLatest { fetchTypes(it) } .collect { println(it) } } if you are using flows you could do something like this – Raghunandan Commented Feb 3 at 3:39
Add a comment  | 

2 Answers 2

Reset to default 0

Your API calls run concurrently, causing fetchTypes() to sometimes use an empty listOfProducts.

One solution would be to use StateFlow to reactively chain the calls and guarantee order:

// ViewModel
private val _products = MutableStateFlow<List<Product>>(emptyList())
val products: StateFlow<List<Product>> = _products

private val _types = MutableStateFlow<List<Type>>(emptyList())
val types: StateFlow<List<Type>> = _types

init {
    viewModelScope.launch {
        // 1. Fetch products first
        _products.value = api.getProducts() 

        // 2. Fetch types AFTER products are loaded
        val typesResponse = api.getTypes() 
        val allType = Type("All", _products.value.size) // Use product count
        _types.value = listOf(allType) + typesResponse 
    }
}

This way getTypes() starts only after getProducts() succeeds.

I feel like you can use the concept of async await . Here is what I tried to do

init {
    viewModelScope.launch {
        callA()
        callB()
    }
}

private suspend fun callA() {
    println("***CallA")
    coroutineScope {
        val deffered = async {
            api1 {
                listOfProducts = it
            }
            listOfProducts
        }
        deffered.await()
        println("***callA result ${listOfProducts.size}")
    }
}

private suspend fun callB() {
    println("***CallB")
    coroutineScope {
        val deffered = async {
            api2 {
                println("***size of product = ${listOfProducts.size}")
                listOfTypes = it
            }
            listOfTypes
        }
        deffered.await()
        println("***callB result ${listOfTypes.size}")
    }
}

suspend fun api1(callback: (List<Product>) -> Unit) {
    println("***api1")
    delay(200L)
    val mockedResponse = listOf(
        Product("a"),
        Product("b"),
        Product("c"),
        Product("d"),
    )
    callback(mockedResponse)
}

suspend fun api2(callback: (List<Type>) -> Unit) {
    println("***api2")
    delay(300L)
    val mockedResponse = listOf(
        Type("a", 1),
        Type("b", 1),
        Type("c", 1),
        Type("d", 1),
    )
    callback(mockedResponse)
}

This is the order of response,

  • ***CallA
  • ***api1
  • ***callA result 4
  • ***CallB
  • ***api2
  • ***size of product = 4
  • ***callB result 4
发布评论

评论列表(0)

  1. 暂无评论