gpt4 book ai didi

kotlin - 在Kotlin中使用协程时,如何保证获取最新数据?

转载 作者:行者123 更新时间:2023-12-02 12:37:26 25 4
gpt4 key购买 nike

代码A来自项目体系结构样本,您可以看到它here
updateTasksFromRemoteDataSource()是暂停函数,因此它可能异步运行。

当我使用参数getTasks(forceUpdate: Boolean)调用函数True时,恐怕return tasksLocalDataSource.getTasks()将在updateTasksFromRemoteDataSource()之前触发。

我不知道代码B是否可以保证return tasksLocalDataSource.getTasks()之后会触发updateTasksFromRemoteDataSource()

代码A

class DefaultTasksRepository(
private val tasksRemoteDataSource: TasksDataSource,
private val tasksLocalDataSource: TasksDataSource,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : TasksRepository {

override suspend fun getTasks(forceUpdate: Boolean): Result<List<Task>> {
// Set app as busy while this function executes.
wrapEspressoIdlingResource {

if (forceUpdate) {
try {
updateTasksFromRemoteDataSource()
} catch (ex: Exception) {
return Result.Error(ex)
}
}
return tasksLocalDataSource.getTasks()
}
}

private suspend fun updateTasksFromRemoteDataSource() {
val remoteTasks = tasksRemoteDataSource.getTasks()

if (remoteTasks is Success) {
// Real apps might want to do a proper sync, deleting, modifying or adding each task.
tasksLocalDataSource.deleteAllTasks()
remoteTasks.data.forEach { task ->
tasksLocalDataSource.saveTask(task)
}
} else if (remoteTasks is Result.Error) {
throw remoteTasks.exception
}
}
...

}

代码B
class DefaultTasksRepository(
private val tasksRemoteDataSource: TasksDataSource,
private val tasksLocalDataSource: TasksDataSource,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : TasksRepository {

override suspend fun getTasks(forceUpdate: Boolean): Result<List<Task>> {
// Set app as busy while this function executes.
wrapEspressoIdlingResource {
coroutineScope {
if (forceUpdate) {
try {
updateTasksFromRemoteDataSource()
} catch (ex: Exception) {
return Result.Error(ex)
}
}
}
return tasksLocalDataSource.getTasks()
}
}

...

}

添加了内容

致Tenfour04:谢谢!

如果有人像Code C一样用 updateTasksFromRemoteDataSource()实现 lauch,当我使用参数 return tasksLocalDataSource.getTasks()调用函数 updateTasksFromRemoteDataSource()时,您确定代码C是 getTasks(forceUpdate: Boolean)将在 True之后触发吗?

代码C
 class DefaultTasksRepository(
private val tasksRemoteDataSource: TasksDataSource,
private val tasksLocalDataSource: TasksDataSource,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : TasksRepository {

override suspend fun getTasks(forceUpdate: Boolean): Result<List<Task>> {
// Set app as busy while this function executes.
wrapEspressoIdlingResource {

if (forceUpdate) {
try {
updateTasksFromRemoteDataSource()
} catch (ex: Exception) {
return Result.Error(ex)
}
}
return tasksLocalDataSource.getTasks()
}
}

private suspend fun updateTasksFromRemoteDataSource() {
val remoteTasks = tasksRemoteDataSource.getTasks()

if (remoteTasks is Success) {
// Real apps might want to do a proper sync, deleting, modifying or adding each task.
tasksLocalDataSource.deleteAllTasks()
launch { //I suppose that launch can be fired
remoteTasks.data.forEach { task ->
tasksLocalDataSource.saveTask(task)
}
}

} else if (remoteTasks is Result.Error) {
throw remoteTasks.exception
}
}
}

新增内容

致乔佛里:谢谢!

我认为代码D可以编译。

在这种情况下,当 forceUpdate为true时,可以在 tasksLocalDataSource.getTasks()完成之前运行 updateTasksFromRemoteDataSource()

代码D
class DefaultTasksRepository(
private val tasksRemoteDataSource: TasksDataSource,
private val tasksLocalDataSource: TasksDataSource,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
private val myCoroutineScope: CoroutineScope
) : TasksRepository {

override suspend fun getTasks(forceUpdate: Boolean): Result<List<Task>> {
// Set app as busy while this function executes.
wrapEspressoIdlingResource {

if (forceUpdate) {
try {
updateTasksFromRemoteDataSource(myCoroutineScope)
} catch (ex: Exception) {
return Result.Error(ex)
}
}
return tasksLocalDataSource.getTasks()
}
}

private suspend fun updateTasksFromRemoteDataSource(myCoroutineScope: CoroutineScope) {
val remoteTasks = tasksRemoteDataSource.getTasks()

if (remoteTasks is Success) {
// Real apps might want to do a proper sync, deleting, modifying or adding each task.
tasksLocalDataSource.deleteAllTasks()
myCoroutineScope.launch {
remoteTasks.data.forEach { task ->
tasksLocalDataSource.saveTask(task)
}
}

} else if (remoteTasks is Result.Error) {
throw remoteTasks.exception
}
}

...
}

最佳答案

从调用站点的 Angular 来看,suspend函数看起来像常规函数,因为它们像常规同步函数一样顺序执行。
我的意思是,对suspend函数的普通调用之后的指令只有在被调用函数完成执行后才会执行。

这意味着代码A很好(当forceUpdate为true时,在完成tasksLocalDataSource.getTasks()之前updateTasksFromRemoteDataSource()永远不会运行),并且代码B中的coroutineScope不必要。

现在关于代码C,结构化并发在这里可以节省您的时间。
如果没有launch接收者,人们根本无法 call CoroutineScope
由于TaskRepository不扩展CoroutineScope,因此按原样编译代码C。

但是,有两种方法可以进行此编译:

  • 使用GlobalScope.launch {}:的确会引起您期望的问题。这样的launch的主体将异步运行,并且与调用者无关。在这种情况下,updateTasksFromRemoteDataSource可以在launch的主体完成之前返回。控制此操作的唯一方法是在对.join()的调用返回的Job上使用launch(等待完成)。这就是为什么通常不建议使用GlobalScope的原因,因为它可以“泄漏”协程。
  • 将对launch的调用包装在coroutineScope {...}内的updateTasksFromRemoteDataSource中。这将确保在coroutineScope调用完成之前,在coroutineScope块中启动的所有协程实际上已完成。注意,尽管取决于coroutineScope / launch的使用方式,但是async块中的所有内容都可以很好地同时运行,但这是首先使用launch的全部意思,不是吗?

  • 现在有了代码D,我对代码C的答案仍然成立。无论您是传递作用域还是使用 GlobalScope,都可以有效地创建比启动它们的暂停函数更长的生命周期的协程。
    因此,它确实会产生您担心的问题。
    但是,如果您不希望实现者在提供的范围内启动长期存在的协程,为什么还要传递 CoroutineScope

    假设您不这样做,那么开发人员不太可能会使用 GlobalScope(或任何范围)来执行此操作。从悬挂函数创建长寿的协程通常是不好的风格。如果您的函数正在挂起,则调用者通常希望它完成时实际上已经完成了工作。

    关于kotlin - 在Kotlin中使用协程时,如何保证获取最新数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61355592/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com