英文:
Suspend multiple async/await function calls until 1 network call returns (Swift)
问题
我在我的应用程序中使用令牌来完成许多任务。当我的令牌过期时,即使多个任务失败并同时请求令牌,我也希望向后端发送1个令牌刷新网络调用。使用完成处理程序很容易实现这一点,因为我可以在第一个令牌刷新请求上发送网络调用,并将后续的令牌刷新请求完成处理程序保存在数组中,直到网络调用返回,然后我会使用新令牌调用所有完成处理程序。是否有一种方法可以暂停多个async/await函数调用,以等待网络调用完成?答案是使用continuations吗?
英文:
I use a token to complete a lot of tasks in my app. When my token expires, I want to send 1 token refresh network call to my backend even if multiple tasks fail and request the token at the same time. This was easy with completion handlers, as I could just send a network call on the first token refresh request and save subsequent token refresh request completion handlers in an array until the network call returned, then I'd call all the completion handlers with the new token. Is there a way to suspend multiple async/await function calls like that while I wait for the network call to complete? Is the answer to use continuations?
答案1
得分: 2
你应该简单地await
一个已保存的Task
。例如:
actor NetworkManager {
var tokenTask: Task<String, Error>?
func token() async throws -> String {
if let tokenTask {
print("awaiting prior request")
return try await tokenTask.value
}
let task = Task {
print("starting request")
// 执行网络请求
}
tokenTask = task
return try await withTaskCancellationHandler {
try await task.value
} onCancel: {
task.cancel()
}
}
}
请注意,因为Task {…}
属于非结构化并发,所以我们希望将等待其value
的部分包装在withTaskCancellationHandler
中,以便调用者有取消任务的选项,如果需要的话。通常情况下,在使用非结构化并发时,我们需要自己处理取消任务的问题。
苹果在WWDC 2021视频《使用Swift actors保护可变状态》的代码中向我们介绍了这种多次在同一个Task
上使用await
的模式。
英文:
You should simply await
a saved Task
. E.g.:
actor NetworkManager {
var tokenTask: Task<String, Error>?
func token() async throws -> String {
if let tokenTask {
print("awaiting prior request")
return try await tokenTask.value
}
let task = Task {
print("starting request")
// perform network request here
}
tokenTask = task
return try await withTaskCancellationHandler {
try await task.value
} onCancel: {
task.cancel()
}
}
}
Note, because Task {…}
is unstructured concurrency, we want to wrap the awaiting of its value
in withTaskCancellationHandler
so that the caller has the option to cancel the task, should it ever need to do so. As a general rule, when using unstructured concurrency, we bear the burden of handling cancelation ourselves.
Apple introduced us to this pattern (of multiple await
on the same Task
) in the code accompanying WWDC 2021 video Protect mutable state with Swift actors.
答案2
得分: 0
你可能想查看CheckedContinuation和UnsafeContinuation。也可以参考Swift进化SE-0300。
当你查看了这些内容后,你可能会意识到async/await可能不是最适合你的问题,因为你实际上想要有一些“条件”,让一个或多个任务等待,而Continuations并不适用于这种情况,因为它们只与一个悬挂点相关联。
根据我的经验,使用Swift Combine可能更适合。甚至使用DispatchQueues(利用suspend()
和resume()
)也可能带来不错的解决方案。
因此,Stack Overflow 真的很喜欢有明确且简洁答案的问题。你的问题有点过于模糊,无法用几行代码回答。我建议你尝试使用Swift Combine来解决问题,如果遇到困难,再回到这里寻求帮助
英文:
You might want to look into CheckedContinuation respectively UnsafeContinuation. See also swift evolution SE-0300.
When you examined that, you might realise that async/await is probably not the best fit for your problem, where you actually want to have some "condition" where one or more tasks can wait on, and Continuations aren't for that, because they are associated to one and only one suspension.
In my experience, using Swift Combine would be a better fit. Even DispatchQueues (leveraging suspend()
and resume()
) might lead to nice solutions.
So, SO really likes questions where there is a clear and crips answer to a specific problem. Your problem is a bit to vague to be answered in a few couple lines of code. I would recommend to approach the problem using Swift Combine, and come back here, when you ran into issues
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论