一个人可以创建一个Task
, 进而cancel
如果它在一定时间内没有完成。例如,并行启动两个任务:
// cancel the fetch after 2 seconds
func fetchThingWithTimeout() async throws -> Thing {
let fetchTask = Task {
try await self.fetchThing() // start fetch
}
let timeoutTask = Task {
try await Task.sleep(for: .seconds(2)) // timeout in 2 seconds
fetchTask.cancel()
}
return try await withTaskCancellationHandler { // handle cancelation by caller of `fetchThingWithTimeout`
let result = try await fetchTask.value
timeoutTask.cancel()
return result
} onCancel: {
fetchTask.cancel()
timeoutTask.cancel()
}
}
// here is a random mockup that will take between 1 and 3 seconds to finish
func fetchThing() async throws -> Thing {
let duration: TimeInterval = .random(in: 1...3)
try await Task.sleep(for: .seconds(duration))
return Thing()
}
If the fetchTask
首先完成,它将到达timeoutTask.cancel
并停止它。如果timeoutTask
首先完成,它将取消fetchTask
.
显然,这取决于《公约》的实施fetchThing
功能。它不仅应该检测取消,还应该抛出错误(可能是CancellationError)如果被取消。如果没有有关实施的细节,我们无法进一步发表评论fetchTask
.
例如,在上面的例子中,而不是返回一个可选的Thing?
,我会返回Thing
,但是拥有它throw
如果被取消,则会出现错误。
注意withTaskCancellationHandler是必需的,因为我们使用了非结构化并发,其中取消不会自动为我们传播。我们必须手动处理这个问题。或者,您可以使用任务组保持结构化并发:
func fetchThingWithTimeout() async throws -> Thing {
try await withThrowingTaskGroup(of: Thing.self) { group in
group.addTask {
try await self.fetchThing() // start fetch
}
group.addTask {
try await Task.sleep(for: .seconds(2)) // timeout in 2 seconds
throw CancellationError()
}
guard let value = try await group.next() else { // see if fetch succeeded …
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
}
group.cancelAll() // … but if we successfully fetched a value, cancel the timeout task, and
return value // … return value
}
}
我犹豫是否要提及这一点,但尽管上面假设fetchThing
表现良好(即可取消),即使不运行,模式上也有一些排列可以工作(即运行doSomethingElse
在一些合理的时间表中,即使fetchThing
“永远不会回来”)。
但这本质上是一种不稳定的情况,因为所使用的资源fetchThing
在完成之前无法恢复。 Swift 不提供抢先取消,因此虽然我们可以轻松解决确保doSomethingElse
最终运行,如果fetchThing
可能永远不会在合理的时间表内完成,你还有更深层次的问题。
你真的应该找到一个演绎something
如果尚未取消,则可以取消。