Kotlin kotlin multiple suspend 可取消协程
Kotlin kotlin multiple suspend cancellable coroutine
我对协程有疑问。我使用一个协程,我想在第一个协程中执行第二个。当我尝试 运行 第二个协程时出现错误:“只能在协程体内调用暂停函数”。我到处都有协程,所以问题出在哪里?这是代码:
suspend fun firstCoroutine(): List<Decks> {
val activeLanguage = userService.getActiveLanguage() ?: return emptyList()
return suspendCancellableCoroutine { continuation ->
db.collection("Decks")
.whereArrayContains("languages", activeLanguage)
.get()
.addOnSuccessListener { documents ->
val items = ArrayList<Decks>()
if (documents != null) {
for (document in documents) {
val id: String = document.id
var knowCountForDeck: Int = secondCoroutine(id). <-- here is problem
val name: String = document.data["name"] as String
val languages: List<String> =
document.data["languages"] as List<String>
items.add(Decks(id, name, languages))
}
}
continuation.resume(items)
}
.addOnFailureListener { err ->
continuation.resumeWithException(err)
}
}
}
suspend fun secondCoroutine(collectionId: String): Int {
val userId = userService.getCurrentUserId() ?: return 0
return suspendCancellableCoroutine { continuation ->
db.collection("Users").document(userId).collection(collectionId).get()
.addOnSuccessListener { cards ->
var knowCountForDeck = 0
if (!cards.isEmpty) {
for (card in cards) {
if (card.data["status"] == "know") {
knowCountForDeck += 1
}
}
}
continuation.resume(knowCountForDeck)
}
.addOnFailureListener { err ->
continuation.resumeWithException(err)
}
}
}
您正在尝试从回调中调用协程,该回调不属于您的协程,因此它无法调用挂起函数。
协程的主要优点之一是您可以避免使用回调并按顺序编写代码。
许多 APIs 已经包含使用回调的挂起函数替代方法。如果我猜你使用的是 Firebase 是正确的,你可以使用挂起函数 await()
而不是使用监听器。那么你不需要使用 suspendCoroutine
或 suspendCancellableCoroutine
将你的回调转换为挂起函数:
suspend fun firstCoroutine(): List<Decks> {
val activeLanguage = userService.getActiveLanguage() ?: return emptyList()
val documents = db.collection("Decks")
.whereArrayContains("languages", activeLanguage)
.get()
.await()
val items = documents.map { document ->
val id: String = document.id
var knowCountForDeck: Int = someSuspendFunction(id)
val name: String = document.data["name"] as String
val languages: List<String> =
document.data["languages"] as List<String>
Decks(id, name, languages)
}
return items
}
如果您使用的 API 没有可用的挂起功能,那么要使用 suspendCoroutine
编写您自己的 API,我建议编写一个可以与任何挂起功能一起使用的基本版本任务,然后在您的特定应用程序代码中使用它。它看起来像这样:
suspend fun <T> Task<T>.await() = suspendCoroutine<T> { continuation ->
addOnSuccessListener {
continuation.resume(it)
}
addOnFailureListener {
continuation.resumeWithException(it)
}
}
或者为了更好地遵循 Kotlin 约定,您可以 return null 而不是在可恢复的故障上抛出异常:
suspend fun <T: Any> Task<T>.awaitResultOrNull(): T? = suspendCoroutine<T> { continuation ->
addOnSuccessListener {
continuation.resume(it)
}
addOnFailureListener {
continuation.resume(null)
}
}
这可以简单地通过将调用移动到调用协程
中的secondCoroutine
函数来解决
yourScope.launch{
val decks = firstCoroutine()
decks.forEach{
val countForDeck = secondCoroutine(it.id)
// Do something with countForDeck
}
}
我对协程有疑问。我使用一个协程,我想在第一个协程中执行第二个。当我尝试 运行 第二个协程时出现错误:“只能在协程体内调用暂停函数”。我到处都有协程,所以问题出在哪里?这是代码:
suspend fun firstCoroutine(): List<Decks> {
val activeLanguage = userService.getActiveLanguage() ?: return emptyList()
return suspendCancellableCoroutine { continuation ->
db.collection("Decks")
.whereArrayContains("languages", activeLanguage)
.get()
.addOnSuccessListener { documents ->
val items = ArrayList<Decks>()
if (documents != null) {
for (document in documents) {
val id: String = document.id
var knowCountForDeck: Int = secondCoroutine(id). <-- here is problem
val name: String = document.data["name"] as String
val languages: List<String> =
document.data["languages"] as List<String>
items.add(Decks(id, name, languages))
}
}
continuation.resume(items)
}
.addOnFailureListener { err ->
continuation.resumeWithException(err)
}
}
}
suspend fun secondCoroutine(collectionId: String): Int {
val userId = userService.getCurrentUserId() ?: return 0
return suspendCancellableCoroutine { continuation ->
db.collection("Users").document(userId).collection(collectionId).get()
.addOnSuccessListener { cards ->
var knowCountForDeck = 0
if (!cards.isEmpty) {
for (card in cards) {
if (card.data["status"] == "know") {
knowCountForDeck += 1
}
}
}
continuation.resume(knowCountForDeck)
}
.addOnFailureListener { err ->
continuation.resumeWithException(err)
}
}
}
您正在尝试从回调中调用协程,该回调不属于您的协程,因此它无法调用挂起函数。
协程的主要优点之一是您可以避免使用回调并按顺序编写代码。
许多 APIs 已经包含使用回调的挂起函数替代方法。如果我猜你使用的是 Firebase 是正确的,你可以使用挂起函数 await()
而不是使用监听器。那么你不需要使用 suspendCoroutine
或 suspendCancellableCoroutine
将你的回调转换为挂起函数:
suspend fun firstCoroutine(): List<Decks> {
val activeLanguage = userService.getActiveLanguage() ?: return emptyList()
val documents = db.collection("Decks")
.whereArrayContains("languages", activeLanguage)
.get()
.await()
val items = documents.map { document ->
val id: String = document.id
var knowCountForDeck: Int = someSuspendFunction(id)
val name: String = document.data["name"] as String
val languages: List<String> =
document.data["languages"] as List<String>
Decks(id, name, languages)
}
return items
}
如果您使用的 API 没有可用的挂起功能,那么要使用 suspendCoroutine
编写您自己的 API,我建议编写一个可以与任何挂起功能一起使用的基本版本任务,然后在您的特定应用程序代码中使用它。它看起来像这样:
suspend fun <T> Task<T>.await() = suspendCoroutine<T> { continuation ->
addOnSuccessListener {
continuation.resume(it)
}
addOnFailureListener {
continuation.resumeWithException(it)
}
}
或者为了更好地遵循 Kotlin 约定,您可以 return null 而不是在可恢复的故障上抛出异常:
suspend fun <T: Any> Task<T>.awaitResultOrNull(): T? = suspendCoroutine<T> { continuation ->
addOnSuccessListener {
continuation.resume(it)
}
addOnFailureListener {
continuation.resume(null)
}
}
这可以简单地通过将调用移动到调用协程
中的secondCoroutine
函数来解决
yourScope.launch{
val decks = firstCoroutine()
decks.forEach{
val countForDeck = secondCoroutine(it.id)
// Do something with countForDeck
}
}