代码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:谢谢!
如果有人像代码C一样用updateTasksFromRemoteDataSource()
实现lauch
,那么您确定在我调用函数{{时,会在return tasksLocalDataSource.getTasks()
之后触发代码C是updateTasksFromRemoteDataSource()
1}}和参数getTasks(forceUpdate: Boolean)
?
代码C
True
新增内容
致乔弗里:谢谢!
我认为代码D可以编译。
在这种情况下,当 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
}
}
}
为真时,forceUpdate
可能会在tasksLocalDataSource.getTasks()
完成之前运行。
代码D
updateTasksFromRemoteDataSource()
答案 0 :(得分:4)
suspend
函数看起来像常规函数,因为它们像常规同步函数一样顺序执行。
我的意思是,在普通调用suspend
函数之后执行的指令要等到被调用函数完成执行后才能执行。
这意味着代码A很好(当forceUpdate
为真,tasksLocalDataSource.getTasks()
永远不会在updateTasksFromRemoteDataSource()
完成之前运行),并且代码B中的coroutineScope
是不必要的
关于代码C,结构化并发可以节省您的时间。
没有launch
接收者,人们根本无法拨打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
(或任何范围)来执行此操作。从悬挂函数创建长寿的协程通常是不好的风格。如果您的函数正在挂起,则调用者通常希望它在完成时实际上已经完成了工作。