kotlin - 在 for 循环中等待所有凌空请求
问题描述
在我的函数中,我需要返回一个由带有一些 Volley 请求的 for 循环填充的列表。所以我需要等待所有这些请求在返回列表之前被终止。
我认为我需要异步 CoroutineScope 来完成这项工作,但我不知道如何等待所有这些响应。
这是我的代码:
suspend fun getListOfAbility(pokemon: Pokemon) : MutableList<Ability> {
val listOfAbility: MutableList<Ability> = emptyList<Ability>() as MutableList<Ability>
CoroutineScope(Dispatchers.IO).launch {
/**
* get the pokemon json
*/
val pokemonJsonObjectRequest = JsonObjectRequest(
Request.Method.GET,
"$pokemonUrl${pokemon.id}",
null,
{
/**
* onResponse
*
* get the list of pokemon abilities
*/
val abilitiesJO = it.getJSONObject("abilities")
val abilityObjectType = object : TypeToken<List<PokemonGson.AbilityObjectGson>>() { }.type
val abilityListGson = Gson().fromJson<List<PokemonGson.AbilityObjectGson>>(abilitiesJO.toString(), abilityObjectType)
/**
* for each ability listed on pokemon info get the full Ability Object
*/
for((index, abilityObjectGson) in abilityListGson.withIndex()) {
val abilityJsonObjectRequest = JsonObjectRequest(
Request.Method.GET,
abilityObjectGson.ability.url,
null,
{
abilityJson ->
/**
* onResponse
*
* get the full ability info
*/
val abilityType = object : TypeToken<AbilityGson>() { }.type
val abilityGson = Gson().fromJson<AbilityGson>(abilityJson.toString(), abilityType)
/**
* fill the Ability entry of listOfAbility with the correct language
*/
val ability = Ability(abilityGson, abilityListGson[index].is_hidden)
listOfAbility.add(ability)
},
{
/**
* onError
*/
Log.d("POKEMON", "Pokemon ability error")
}
)
requestQueue.add(abilityJsonObjectRequest)
}
},
{
/**
* onError
*/
Log.d("POKEMON", "Pokemon request error")
}
)
requestQueue.add(pokemonJsonObjectRequest)
}
//wait
return listOfAbility
}
解决方案
要在挂起函数中使用基于回调的代码,您需要使用suspendCoroutine
or将其转换为挂起函数suspendCancellableCoroutine
。因此,在这种情况下,要替换创建 JSONObjectRequest 和侦听器、将其排队到 RequestQueue 并以某种方式等待它的操作,我将创建一个如下所示的挂起函数:
suspend inline fun RequestQueue.getJSONObjectOrNull(
method: Int,
url: String,
jsonRequest: JSONObject?,
crossinline onError: (VolleyError)->Unit = {}
): JSONObject? = suspendCancellableCoroutine { continuation ->
val request = JsonObjectRequest(
method,
url,
jsonRequest,
{ result: JSONObject -> continuation.resume(result) },
{ error ->
onError(error)
continuation.resume(null)
}
)
add(request)
continuation.invokeOnCancellation { request.cancel() }
}
它直接返回 JSONObject 结果,如果失败则返回 null。如果您想记录错误,您可以选择对错误运行回调。
然后,您可以使用它来编写函数的更顺序版本,而不是基于回调的版本。您可以使用 的模式coroutineScope { async { list.map { ... } } }.awaitAll()
将列表的每个项目转换为使用并行协程的其他项目。
这是您的功能的未经测试的版本。我让它在失败时返回一个空列表。您也可以在失败时返回 null,这可能更有用,因此调用函数可以决定在失败时执行不同的操作。
private fun VolleyError.logDebug() {
Log.d("POKEMON", "Pokemon request error: $this")
}
suspend fun getListOfAbility(pokemon: Pokemon): List<Ability> {
val pokemonJsonObject = requestQueue.getJSONObjectOrNull(Request.Method.GET, "$pokemonUrl${pokemon.id}", null, VolleyError::logDebug)
pokemonJsonObject ?: return emptyList()
val abilitiesJO = pokemonJsonObject.getJSONObject("abilities")
val abilityObjectType = object : TypeToken<List<PokemonGson.AbilityObjectGson>>() {}.type
val abilityListGson: List<Wrapper> = Gson().fromJson<List<PokemonGson.AbilityObjectGson>>(
abilitiesJO.toString(),
abilityObjectType
)
return coroutineScope {
abilityListGson.map {
async {
requestQueue.getJSONObjectOrNull(Request.Method.GET, it.ability.url, null, VolleyError::logDebug)
}
}
}
.awaitAll()
.filterNotNull()
.map { abilityJson ->
val abilityType = object : TypeToken<AbilityGson>() {}.type
val abilityGson = Gson().fromJson<AbilityGson>(abilityJson.toString(), abilityType)
Ability(abilityGson, abilityListGson[index].is_hidden)
}
}
推荐阅读
- javascript - 需要 MapBox GL 访问令牌
- ruby-on-rails - HTTP Origin 标头 (https://example.com) 与 request.base_url (http://example.com) rails 不匹配
- excel - Excel 数据透视表,行中有许多列标题
- jsf - h:使用 FullAjaxExceptionHandler 时未插入输出样式表
- java - 支持颜色的 Markdown 替代品
- python - 选择 DataFrame 行:为什么结果用 NaN 值填充?
- git - 轮询 SCM 以获取特定分支 Jenkins
- c - 把一个词变成***符号
- android - 如何在 Android 上使用 WebRTC 将相机预览渲染到 SurfaceTexture (openGL)
- javascript - VueJS 点击在移动设备上不起作用,未检测到点击监听器