Bic*_*ops 4 concurrency async-await swift
向等待函数添加超时的最佳方法是什么?
Example:
/// lets pretend this is in a library that I'm using and I can't mess with the guts of this thing
func fetchSomething() async -> Thing? {
// fetches something
}
// if fetchSomething() never returns then doSomethingElse() is never ran. Is there anyway to add a timeout to this system?
let thing = await fetchSomething()
doSomethingElse()
Run Code Online (Sandbox Code Playgroud)
我想让系统在 fetchSomething() 永远不会返回的情况下更加健壮。如果这是使用组合,我会使用超时运算符。
可以创建一个Task,cancel如果在某一段时间内没有完成则再创建一个 。例如,并行启动两个任务:
// cancel the fetch after 2 seconds\n\nfunc fetchThingWithTimeout() async throws -> Thing {\n let fetchTask = Task {\n try await self.fetchThing() // start fetch\n }\n\n let timeoutTask = Task {\n try await Task.sleep(for: .seconds(2)) // timeout in 2 seconds\n fetchTask.cancel()\n }\n\n return try await withTaskCancellationHandler { // handle cancelation by caller of `fetchThingWithTimeout`\n let result = try await fetchTask.value\n timeoutTask.cancel()\n\n return result\n } onCancel: {\n fetchTask.cancel()\n timeoutTask.cancel()\n }\n}\n\n// here is a random mockup that will take between 1 and 3 seconds to finish\n\nfunc fetchThing() async throws -> Thing {\n let duration: TimeInterval = .random(in: 1...3)\n try await Task.sleep(for: .seconds(duration))\n return Thing()\n}\nRun Code Online (Sandbox Code Playgroud)\n如果fetchTask先完成,它将到达timeoutTask.cancel并停止它。如果timeoutTask先完成,它将取消fetchTask.
显然,这取决于函数的实现fetchThing。它不仅应该检测取消,还应该CancellationError在取消时抛出错误(可能是 )。如果没有有关实施的细节,我们无法进一步发表评论fetchTask。
例如,在上面的示例中,Thing?我不会返回可选的,而是返回Thing,但如果它被取消,则会出现throw错误。
请注意,这withTaskCancellationHandler是必需的,因为我们使用非结构化并发,其中取消不会自动为我们传播。我们必须手动处理这个问题。或者,您可以使用任务组保持结构化并发:
func fetchThingWithTimeout() async throws -> Thing {\n try await withThrowingTaskGroup(of: Thing.self) { group in\n group.addTask {\n try await self.fetchThing() // start fetch\n }\n\n group.addTask {\n try await Task.sleep(for: .seconds(2)) // timeout in 2 seconds\n throw CancellationError()\n }\n\n guard let value = try await group.next() else { // see if fetch succeeded \xe2\x80\xa6\n throw FetchError.noData // theoretically, it should not be possible to get here (as we either return a value or throw an error), but just in case\n }\n\n group.cancelAll() // \xe2\x80\xa6 but if we successfully fetched a value, cancel the timeout task, and\n return value // \xe2\x80\xa6 return value\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n我犹豫是否要提及它,但是虽然上面假设fetchThing行为良好(即可取消),但即使不起作用,模式上也有一些排列可以工作(即,doSomethingElse即使fetchThing\xe2\x80\在某个合理的时间表中运行) x9c 永远不会返回\xe2\x80\x9d)。
但这本质上是一种不稳定的情况,因为fetchThing在完成之前所使用的资源无法恢复。Swift 不提供抢先取消,因此虽然我们可以轻松解决确保doSomethingElse最终运行的战术问题,但如果fetchThing可能永远不会在某个合理的时间表内完成,那么您会遇到更深层次的问题。
你确实应该找到一个something可以取消的版本(如果还没有的话)。
| 归档时间: |
|
| 查看次数: |
1639 次 |
| 最近记录: |