Pit*_*Pan 3 task grand-central-dispatch async-await swift urlsession
您好,我有一个情况,我需要在多个任务中调用相同的方法。我希望能够一一调用此方法(同步)而不是在并行模式下。看起来像这样:
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)
}
Run Code Online (Sandbox Code Playgroud)
输出:
尝试刷新:1
尝试刷新:2
我所需的输出:
尝试刷新:1 或尝试刷新 2。取决于第一个任务被调用。
有任何想法吗?
你说:
\n\n\n我希望[第二次尝试]等待第一次刷新 API 完成
\n
您可以保存对您的引用Task(如果找到)await。如果没有找到,则开始任务。(并且因为我们使用的是非结构化并发,所以请记住将其包装在 . 中withTaskCancellationHandler。)
另外,我个人会将等待/取消内容的逻辑移至 \xe2\x80\x9crefresh\xe2\x80\x9d 进程中,而不是 API 调用代码中。因此:
\nactor Refresh {\n var priorTask: Task<Void, Error>?\n\n func refresh(value: Int) async throws {\n if let priorTask {\n _ = try await priorTask.value\n return\n }\n\n let task = Task {\n try await mockCallAPI(value: value)\n }\n\n priorTask = task\n\n try await withTaskCancellationHandler {\n _ = try await task.value\n priorTask = nil\n } onCancel: {\n task.cancel()\n }\n }\n\n private func mockCallAPI(value: Int) async throws {\n try await Task.sleep(for: .seconds(0.1)) // imitation API CALL\n print("Try to refresh: \\(value)")\n }\n}\nRun Code Online (Sandbox Code Playgroud)\nApple 在与 WWDC 2021 视频“使用 Swift Actor 保护可变状态”相关的代码中展示了这种模式的示例。
\n他们的示例更复杂(一种避免某些图像缓存/下载程序发起重复网络请求的模式),但其思想的核心是相同的:保存和await.Task
请注意,上面的内容是围绕原始问题设计的,以返回第一个请求\xe2\x80\x99s 结果,并避免在前一个请求正在进行时启动后续请求。这种模式在缓存结果模式中很常见,在这种模式中,您可能有重复的请求,所有请求都返回完全相同的结果(例如,来自 CDN 的某些静态资源,例如在 Apple 示例中)。
\n但是当我们谈论 \xe2\x80\x9crefresh\xe2\x80\x9d 进程时,用户通常想要最新的结果。对于 \xe2\x80\x9crefresh\xe2\x80\x9d,我们通常不希望向用户显示先前请求的旧的、可能已经过时的结果。因此,在刷新时,我们通常希望取消之前的请求并开始一个新的请求:
\nactor Refresh {\n var priorTask: Task<Void, Error>?\n\n func refresh(value: Int) async throws {\n let task = Task { [priorTask] in\n priorTask?.cancel()\n try await mockCallAPI(value: value)\n }\n\n priorTask = task\n\n try await withTaskCancellationHandler {\n _ = try await task.value\n } onCancel: {\n task.cancel()\n }\n }\n\n private func mockCallAPI(value: Int) async throws {\xe2\x80\xa6}\n}\nRun Code Online (Sandbox Code Playgroud)\n这是一个微妙的点,但请注意,我们希望捕获priorTask以避免对此刷新过程的多个调用之间的竞争。
因此,您有以下两个选择: (a) 等待/返回第一个请求并避免重复请求;(b) 取消先前的请求并发起新的请求,确保刷新返回最新的结果。一般来说,当结果是静态的时,我们会倾向于使用第一种模式,而当结果可能随时间变化时,我们会倾向于使用后者。使用术语 \xe2\x80\x9crefresh\xe2\x80\x9d 通常意味着我们想要最新的结果,但这完全取决于您。我只是想展示这两种模式。
\n| 归档时间: |
|
| 查看次数: |
1377 次 |
| 最近记录: |