你好,我有一个案例,我需要在多个任务中调用相同的方法。我希望有一个可能性调用这个方法一个接一个(同步),而不是在并行模式。看起来像这样:
var isReadyToRefresh: Bool = true
func refresh(value: Int) async {
try! await Task.sleep(nanoseconds: 100_000_000) // imitation API CALL
isReadyToRefresh = false
print("Try to refresh: \(value)")
}
func mockCallAPI(value: Int) async {
if isReadyToRefresh {
await refresh(value: value)
}
}
Task {
await mockCallAPI(value: 1)
}
Task {
await mockCallAPI(value: 2)
}
产出:
尝试刷新:1
尝试刷新:2
我需要的输出:
尝试刷新:% 1或尝试刷新% 2。取决于哪个任务被作为第一个任务调用。
有什么想法吗?
2条答案
按热度按时间vmdwslir1#
你说:
我希望[第二次尝试]等待第一次刷新API完成
您可以保存对
Task
的引用,如果找到,则await
它。如果没有找到,则启动任务。(因为我们使用的是非结构化并发,所以请记住将其 Package 在withTaskCancellationHandler
中。另外,我个人会将关于等待/取消的逻辑移动到“刷新”过程中,而不是API调用代码中。由此可见:
苹果在与WWDC 2021视频相关的代码中展示了这种模式的一个例子,Protect mutable state with Swift actors。
他们的例子更复杂(一种避免重复网络请求被某些图像缓存/下载器发起的模式),但其思想的核心是相同的:保存并
await
Task
。请注意,上面是围绕原始问题设计的,以返回第一个请求的结果,并避免在前一个请求正在进行时启动后续请求。这种模式在缓存结果模式中很常见,在这种模式中,您可能会有重复的请求,所有这些请求都返回完全相同的结果(例如,来自CDN的一些静态资源,例如Apple示例中的静态资源)。
但是当我们谈论“刷新”过程时,用户通常想要最新的结果。使用“刷新”,我们通常不想向用户显示先前请求的较旧的、可能过期的结果。因此,在刷新时,我们通常希望取消先前的请求并启动新的请求:
这一点很微妙,但请注意,我们希望捕获
priorTask
,以避免多次调用该刷新进程之间的竞争。所以,你有这两个选择:(a)等待/返回第一个请求并避免重复请求;或者(B)取消先前的请求并发起新的请求,确保刷新返回最新的结果。一般来说,当结果是静态的时,我们倾向于第一种模式,当结果可能随时间变化时,我们倾向于第二种模式。使用术语“刷新”通常意味着我们想要最新的结果,但这完全取决于您。我只想展示这两种模式。
rekjcdws2#
如果你不想让它们并行运行,为什么它们需要在单独的任务中呢?await意味着代码在任务完成后不会有任何进展,因为协作线程启动它的线程可能会被用来做其他事情,比如更多地使用交互处理,或者其他任务,事实上,因为你把它们放在单独的任务中,你要求它们并行运行,包含它们的块可能会进入非常bringing,并且您将有另外两个任务,在这种情况下,您需要等待来自它们容器任务的结果,说它们已经完成,并且进入块的代码检查此以继续任何进一步。