suspendCoroutine和suspendCancellableCoroutine使用

2,566 阅读2分钟

1. suspendCoroutine作用及用法

suspendCoroutine 是 Kotlin 中用于挂起协程并与外部世界进行交互的函数之一。它的主要作用是允许你在协程中将异步非挂起的操作转化为挂起操作,从而可以方便地与协程上下文集成。具体来说,suspendCoroutine 的主要作用包括以下几点:

  1. 将回调式的异步操作转化为挂起操作:当你需要在协程中执行某些异步操作,例如调用回调函数,处理回调式 API,或等待某个事件发生时,suspendCoroutine 允许你将这些异步操作封装为挂起操作,使得你可以像调用普通的挂起函数一样调用它们,从而简化了异步编程的代码。
  2. 挂起当前协程:suspendCoroutine 在执行时会挂起当前协程,这意味着协程的执行会暂停,直到回调操作完成。这有助于防止阻塞线程,并允许其他协程在执行。
  3. 传递回调函数:suspendCoroutine 接受一个 Lambda 表达式,该 Lambda 表达式需要传递一个函数参数,通常是一个回调函数,该回调函数将在异步操作完成时被调用。在 Lambda 表达式内部,你可以使用 resume 来恢复协程的执行并传递结果,或者使用 resumeWithException 来传递异常,以便协程可以处理结果或错误。

2. suspendCoroutine案例

suspend fun fetchUserDataAsync(): Deferred<String> {
    return CoroutineScope(Dispatchers.IO).async {
        // 模拟异步操作
        delay(1000)
        "User data fetched"
    }
}
suspend fun fetchData(): String {
    return suspendCoroutine { continuation ->
        val deferred = fetchUserDataAsync()

        deferred.invokeOnCompletion {
            if (it == null) {
                val result = deferred.getCompleted()
                continuation.resume(result)//返回正常结果,也就是一个String
            } else {
                continuation.resumeWithException(it)//返回异常,需要在函数调用方捕获
            }
        }
    }
}
fun main() = runBlocking {
    try {
        val data = fetchData()
        println(data)
    } catch (e: Exception) {
        println("Error: ${e.message}")
    }
}

3. suspendCancellableCoroutine作用和用法

suspendCancellableCoroutine 是 Kotlin 中的一个函数,它允许你在一个协程中创建一个可取消的挂起操作,通常用于实现自定义的挂起函数或处理异步操作。基本功能和suspendCoroutine一样,只是多了一个支持监听外部协程取消,进而取消内部协程执行的能力。

4. suspendCancellableCoroutine案例

import kotlinx.coroutines.*
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine

// 自定义的可取消挂起函数
suspend fun customCancelableOperation(): String {
    return suspendCancellableCoroutine { continuation ->
        val job = GlobalScope.launch {
            try {
                // 模拟异步操作
                delay(1000)
                // 如果操作成功,恢复挂起操作并传递结果
                continuation.resume("Operation completed successfully")
            } catch (e: CancellationException) {
                // 如果操作被取消,处理取消操作
                continuation.resumeWithException(e)
            } catch (e: Exception) {
                // 如果操作失败,传递异常
                continuation.resumeWithException(e)
            }
        }

        // 当外部协程取消时,取消内部协程
        continuation.invokeOnCancellation {
            job.cancel()
        }
    }
}

fun main() = runBlocking {
    val job = launch {
        val result = customCancelableOperation()
        println(result)
    }

    delay(500) // 等待一段时间
    job.cancel() // 取消外部协程
    job.join()
}

5. 处理resumeWithException的通用方案

除了通过try catch的方式来捕获异常,也可以在launch协程的content中添加exceptionHandler来统一处理异常。 例如第一个例子中可以如下改写:

fun main() = runBlocking {
    val exceptionHandler = CoroutineExceptionHandler { _, exception ->
        println("Caught exception: $exception")
    }

    val job = GlobalScope.launch(exceptionHandler) {
        val data = fetchData()
        println(data)
    }

    job.join()
}