我正在使用协程编写一个应用程序(下面的代码已大大简化)。最近看了Coroutines in Practice说话有点糊涂了。原来我不知道什么时候使用 CoroutineScope
的扩展函数,什么时候使用挂起函数。
我有一个实现了 CoroutineScope 的 mediator(Presenter/ViewModel/Controller/etc):
class UiMediator : CoroutineScope {
private val lifecycleJob: Job = Job()
override val coroutineContext = lifecycleJob + CoroutineDispatchersProvider.MAIN
// cancel parent Job somewhere
fun getChannel() {
launch {
val channel = useCase.execute()
view.show(channel)
}
}
}
业务逻辑(交互器/用例):
class UseCase {
suspend fun execute(): RssChannel = repository.getRssChannel()
}
还有一个存储库:
class Repository {
suspend fun getRssChannel(): RssChannel {
// `getAllChannels` is a suspending fun that uses `withContext(IO)`
val channels = localStore.getAllChannels()
if (channels.isNotEmpty()) {
return channels[0]
}
// `fetchChannel` is a suspending fun that uses `suspendCancellableCoroutine`
// `saveChannel` is a suspending fun that uses `withContext(IO)`
return remoteStore.fetchChannel()
.also { localStore.saveChannel(it) }
}
}
所以我有几个问题:
- 我是否应该将
Repository#getRssChannel
声明为 CoroutineScope 的扩展函数(因为 它产生了新的暂停函数:getAllChannels
,fetchChannel
,saveChannel
)?那么如何在UseCase
中使用它呢? - 我是否应该将
Repository#getRssChannel
包装到一个coroutineScope
函数使所有生成的暂停 函数是后者的 child ? - 或者也许它已经很好了,我什么都不应该改变。何时
然后将函数声明为
CoroutineScope
的扩展?
最佳答案
问题 1 的答案:
不,你不应该不将Repository#getRssChannel
声明为CoroutineScope
的扩展函数,因为你只调用挂起函数而不是启动( launch
/async
) 新作业。正如@Francesc 解释的那样,CoroutineScope
的扩展函数应该只开始新的工作,但不能立即返回结果,不应该声明为 suspend
本身。
问题 2 的答案:
不,您应该不将Repository#getRssChannel
包装到CoroutineScope
中。仅当您在此方法中启动 (launch
/async
) 新协程时,包装才有意义。新作业将是当前作业的子作业,外部方法只会在所有并行作业完成后返回。在您的情况下,您有其他暂停协程的顺序调用,并且不需要新的范围。
问题 3 的答案:
是的,您可以保留您的代码。如果您多次需要 UiMediator#getChannel
的功能,那么此方法将是 CoroutineScope
的扩展函数的候选方法。
关于kotlin - 应使用 CoroutineScope 的扩展函数或挂起函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53770772/