在 Kotlin 中将回调函数转换为可暂停的协程

4

我目前有一个回调函数,它不会同步返回值,而是需要进行一些异步工作,然后返回结果。我一直在研究如何做到这一点,但我还没有弄清楚。到目前为止,我已经达到了这个目标,但由于我需要在foo方法中返回一个值,所以首先它返回“something”,然后再返回调用someMethod()的结果。

我是否能够实现我想要的功能?

  class CustomClass() :
    SomeClass.SomeInterface {

    override fun foo(p0: SomeType): String {
      val result = "something"
      MainScope().launch {
        withContext(Dispatchers.IO) {
          result = someMethod()
        }
      }
      return result
    }

    suspend fun someMethod(): String =
      suspendCancellableCoroutine { cancelableContinuation ->
        //TODO: register some listener that will end up calling 
        cancelableContinuation.resume("some_value", {})
      }

提前致谢!


你的意思是你的方法将返回不同种类的状态或值吗?如果是,你可能正在寻找一个Flow - momvart
不,它应该每次回调被调用时只返回一个值。 - strok_777
2个回答

3

由于无法同步返回结果,因此必须将foo定义为挂起函数。

另外,在withContext中包装挂起函数调用是没有必要的。挂起函数(如果正确组合)在内部包装任何阻塞代码,因此可以从任何调度程序安全地调用它们,因此没有必要使用withContext指定调度程序。

如果去掉withContext和你不等待的协程启动,foo()除了调用someMethod之外什么也不做,因此可以将该代码从someMethod移动到foo中。但foo必须定义为suspend函数。

override suspend fun foo(p0: SomeType): String = suspendCancellableCoroutine { continuation ->
    val request = someApi.request(p0) { result: String ->
        continuation.resume(result)
    }
    continuation.invokeOnCancellation {
        request.cancel()
    }
}

通常情况下,您不希望像这样的模型类从它们自己的CoroutineScopes中启动协程。只需让它们公开暂停函数即可。由Activity、Fragment和ViewModel等更高级别的类来从它们所需的范围内启动协程,以便它们可以控制协程的生命周期。


你不能同步返回结果,所以foo必须被定义为一个挂起函数。很抱歉,我对你的回复有些疑惑。如果我按照你提到的方式进行编写,我会收到警告信息:“挂起函数'suspendCancellableCoroutine'只能从协程或另一个挂起函数中调用”。问题是,我正在使用React Native,所以我不清楚Activity / Fragment或ViewModel的生命周期。我的尝试是将这个管理委托给某个扩展接口的对象,并且需要通过异步操作来返回其值。 - strok_777
我忘了提到,foo 本身必须是一个挂起函数。因此,您必须在接口中将其定义为 suspend。当您调用 foo 时,您将从源启动的协程中执行它。如果您正在使用 Activity/Fragment,则通常是 lifecycleScopeviewLifecycleScope,或者在 ViewModel 中,那就是 viewModelScope。您不必对各种生命周期非常熟悉才能使用它们。它们在其生命周期结束时会自动取消。 - Tenfour04
好的,我的意思是我不拥有接口SomeClass.SomeInterface,所以我无法将foo修改为挂起函数。 - strok_777
有什么建议吗? - strok_777

1

你的foo函数基本上是同步的,所以正在发生的是你启动了一个suspendable,不等待它的结果并返回结果的初始值("something"

有多种方法可以确保foo等待来自someMethod的响应,例如:

  • foo变为`suspend fun`
  • 使用回调,例如:
override fun foo(p0: SomeType, onResult: (String) -> Unit): String {
      MainScope().launch {
        withContext(Dispatchers.IO) {
          onResult(someMethod())
        }
      }
    }

// Call it like this:
foo(p0) { result ->
    // handle result
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接