英文:
Call a API synchronously inside For Each loop in android Kotlin Coroutines with Flow getting Error
问题
在 Android Kotlin Coroutines 中,在 For-each 循环中同步调用 API 并使用 Flow 进行操作。此外,需要等待所有 API 调用完成,而不阻塞主线程。
fun getLatesData(): Flow<Model> = flow {
emit(getFirstList())
}.flatMapLatest { data ->
flow {
val responseList = mutableListOf<String>()
data.items?.forEach { datalist ->
val response = getCreatedDate(datalist.Id)
response.collect { count ->
responseList.add(count.check)
}
val countData = Model(
datalist = data,
responseListCount = responseList
)
}
emit(countData)
}
}
私有函数 getCreatedDate(Id: String?)
用于调用 API:
private fun getCreatedDate(Id: String?) = flow {
try {
val response = repoApi.getCount(Id)
emit(response)
} catch (e: Exception) {
e.printStackTrace()
}
}.flowOn(Dispatchers.IO)
API 请求接口示例:
@Headers("Content-Type: application/json")
@GET("getCount/{Id}")
suspend fun getCount(@Path("Id") Id: String?): Response
请注意,第一次迭代时数据正常发出,但第二次迭代后出现上述错误。
在 For 循环中使用 Flow 运行 API 调用,而不阻塞 UI 线程。提前感谢。
英文:
Call a API synchronously inside For-each loop in android Kotlin Coroutines using Flow.
Also i need to wait all the API calls need to complete without blocking the Main Thread.
fun getLatesData(): Flow<Model> = flow {
emit(getFirstList())
}.flatMapLatest { data ->
flow {
val responseList = mutableListOf<String>()
data.items?.forEach { datalist ->
val response = getCreatedDate(datalist.Id)
response.collect{count->
responseList.add(count.check)
}
val countData = Model(
datalist = data,
responseListCount = responseList
)
}
emit(countData)
}
}
private fun getCreatedDate(Id: String?)= flow {
try {
val response = repoApi.getCount(Id)
emit(response)
} catch (e: Exception) {
e.printStackTrace()
}
}.flowOn(Dispatchers.IO)
@Headers(
"Content-Type: application/json"
)
@GET("getCount/{Id}")
suspend fun getCount(@Path("Id")Id: String?): response
ERROR-kotlinx.coroutines.flow.internal.AbortFlowException: Flow was aborted, no more elements needed
For the first iteration Data is emitting properly, but second time onwards getting above mentioned error.
Run a API call without blocking the UI thread inside for loop with Flow. Thank in advance.
答案1
得分: 1
不需要创建额外的 flow
并收集它,您可以将 getCreatedDate
转换为挂起函数,然后轻松在 flow
构建器的 for-loop
内部使用它。
@OptIn(ExperimentalCoroutinesApi::class)
fun getLatestData(): Flow<Model> = flow {
emit(getFirstList())
}.flatMapLatest { data ->
flow {
val responseList = data.items?.mapNotNull { getCreatedDate(it.Id)?.check }.orEmpty()
emit(Model(dataList = data, responseListCount = responseList))
}
}
private suspend fun getCreatedDate(Id: String?): Response? =
try {
repoApi.getCount(Id)
} catch (e: Exception) {
e.printStackTrace()
null
}
英文:
No need to create an extra flow
and collect it, you could convert getCreatedDate
to suspend instead and easily use it inside for-loop
of flow
builder
@OptIn(ExperimentalCoroutinesApi::class)
fun getLatestData(): Flow<Model> = flow {
emit(getFirstList())
}.flatMapLatest { data ->
flow {
val responseList = data.items?.mapNotNull { getCreatedDate(it.Id)?.check }.orEmpty()
emit(Model(dataList = data, responseListCount = responseList))
}
}
private suspend fun getCreatedDate(Id: String?): Response? =
try { repoApi.getCount(Id) }
catch (e: Exception) {
e.printStackTrace()
null
}
答案2
得分: 1
It doesn't make sense for getCreatedDate()
to return a flow. A Flow is for a stream of values, not a single value. It can just be a suspend function.
private suspend fun getCreatedDate(id: String?) =
try {
repoApi.getCount(id)
} catch (e: Exception) {
// Catching all exceptions is an antipattern. Be specific and rethrow unexpected ones.
if (e !is IOException || e !is HttpException) throw e
e.printStackTrace()
null
}
Your other code can be simplified. No reason to create a flow of a single item and then flat map it. Just create the single flow directly. And use mapNotNull
to more simply create your response list.
suspend fun getLatesData(): Model {
val data = getFirstList()
val responseList = data.items?.mapNotNull { getCreatedDate(it.id)?.check }.orEmpty()
return Model(
datalist = data,
responseListCount = responseList
)
}
If the API can handle doing the requests in parallel, this may be faster:
suspend fun getLatesData(): Model {
val data = getFirstList()
val responseList = coroutineScope {
data.items?.mapNotNull { async { getCreatedDate(it.id)?.check } }.orEmpty()
}.awaitAll()
return Model(
datalist = data,
responseListCount = responseList
)
}
英文:
It doesn't make sense for getCreatedDate()
to return a flow. A Flow is for a stream of values, not a single value. It can just be a suspend function.
private fun getCreatedDate(id: String?) =
try {
repoApi.getCount(id)
} catch (e: Exception) {
// Catching all exceptions is an antipattern. Be specific and rethrow unexpected ones.
if (e !is IOException || e !is HttpException) throw e
e.printStackTrace()
null
}
Your other code can be simplified. No reason to create a flow of a single item and then flat map it. Just create the single flow directly. And use mapNotNull
to more simply create your response list.
fun getLatesData(): Flow<Model> = flow {
val data = getFirstList()
val responseList = data.items?.mapNotNull { getCreatedDate(it.id)?.check }.orEmpty()
val countData = Model(
datalist = data,
responseListCount = responseList
)
emit(countData)
}
Edit: I just realized that we are ultimately ending up with a Flow of a single item here, which is the same nonsensical situation as I mentioned about getCreatedDate()
. The whole thing should be a suspend function like this:
suspend fun getLatesData(): Model {
val data = getFirstList()
val responseList = data.items?.mapNotNull { getCreatedDate(it.id)?.check }.orEmpty()
return Model(
datalist = data,
responseListCount = responseList
)
}
If the API can handle doing the requests in parallel, this may be faster:
suspend fun getLatesData(): Model {
val data = getFirstList()
val responseList = coroutineScope {
data.items?.mapNotNull { async { getCreatedDate(it.id)?.check } }.orEmpty()
}.awaitAll()
return Model(
datalist = data,
responseListCount = responseList
)
}
答案3
得分: 1
以下是代码部分的中文翻译:
当流被中止时,协程框架会抛出 AbortFlowException 来指示流收集应该停止。这个异常在流框架内部被捕获。只需添加挂起函数。
suspend fun getLatesData(): Flow<Model> = flow {
emit(getFirstList())
}.flatMapLatest { data ->
flow {
val responseList = mutableListOf<String>()
data.items?.forEach { datalist ->
val response = getCreatedDate(datalist.Id)
response.collect { count ->
responseList.add(count.check)
}
val countData = Model(
datalist = data,
responseListCount = responseList
)
}
emit(countData)
}
}
private suspend fun getCreatedDate(Id: String?) = flow {
try {
val response = repoApi.getCount(Id)
emit(response)
} catch (e: Exception) {
e.printStackTrace()
}
}.flowOn(Dispatchers.IO)
@Headers(
"Content-Type: application/json"
)
@GET("getCount/{Id}")
suspend fun getCount(@Path("Id") Id: String?): Response
英文:
When a flow is aborted, the Coroutines framework throws the AbortFlowException to indicate that the flow collection should be stopped. This exception is caught internally by the flow framework. Just add suspend function.
suspend fun getLatesData(): Flow<Model> = flow {
emit(getFirstList())
}.flatMapLatest { data ->
flow {
val responseList = mutableListOf<String>()
data.items?.forEach { datalist ->
val response = getCreatedDate(datalist.Id)
response.collect{count->
responseList.add(count.check)
}
val countData = Model(
datalist = data,
responseListCount = responseList
)
}
emit(countData)
}
}
private suspend fun getCreatedDate(Id: String?)= flow {
try {
val response = repoApi.getCount(Id)
emit(response)
} catch (e: Exception) {
e.printStackTrace()
}
}.flowOn(Dispatchers.IO)
@Headers(
"Content-Type: application/json"
)
@GET("getCount/{Id}")
suspend fun getCount(@Path("Id")Id: String?): response
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论