取决于您需要的超时类型,但我制定了其中一种超时方法。这种方法通过超时发生时取消当前任务,并继续向前丢弃等待任务。
取消任务实际上并没有停止当前操作,只是告诉它们已取消。
下面的代码调用取消任务,但“完成”打印将在longOperation完成后才完成,除非该操作具有取消处理。
let task = Task {
await longOperation()
print("done")
}
...
task.cancel()
那么,让我们来想象以下情况。
await fetchFlag()
applyFlag()
这个案例需要在任何情况下都在5秒内获取标志的超时。
是否提出取消请求实际上停止当前操作并弹出它们的步骤取决于实现。
为了以任何代价丢弃处理它们而使超时。
(所以实际上获取请求仍在进行中,但仍然向前移动,因为已超时。)
await withTimeout(5) {
await fetchFlag()
}
applyFlag()
为了实现这种行为,需要在内部利用非结构化并发API。
public enum TimeoutHandlerError: Error {
case timeoutOccured
}
@_unsafeInheritExecutor
public func withTimeout<Return: Sendable>(
nanoseconds: UInt64,
@_inheritActorContext _ operation: @escaping @Sendable () async throws -> Return
) async throws -> Return {
let task = Ref<Task<(), Never>>(value: nil)
let timeoutTask = Ref<Task<(), any Error>>(value: nil)
let flag = Flag()
return try await withTaskCancellationHandler {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Return, Error>) in
do {
try Task.checkCancellation()
} catch {
continuation.resume(throwing: error)
return
}
let _task = Task {
do {
let taskResult = try await operation()
await flag.performIf(expected: false) {
continuation.resume(returning: taskResult)
return true
}
} catch {
await flag.performIf(expected: false) {
continuation.resume(throwing: error)
return true
}
}
}
task.value = _task
let _timeoutTask = Task {
try await Task.sleep(nanoseconds: nanoseconds)
_task.cancel()
await flag.performIf(expected: false) {
continuation.resume(throwing: TimeoutHandlerError.timeoutOccured)
return true
}
}
timeoutTask.value = _timeoutTask
}
} onCancel: {
task.value?.cancel()
timeoutTask.value?.cancel()
}
}
private final class Ref<T>: @unchecked Sendable {
var value: T?
init(value: T?) {
self.value = value
}
}
private actor Flag {
var value: Bool = false
func set(value: Bool) {
self.value = value
}
func performIf(expected: Bool, perform: @Sendable () -> Bool) {
if value == expected {
value = perform()
}
}
}
https://github.com/muukii/swift-async-timeout/blob/main/Sources/swift-async-timeout/swift_async_timeout.swift
search
是可取消的(而它没有抛出错误的事实表明它是可取消的)。它是可取消的吗? - Robsearch
可取消(即在取消时停止并可能抛出CancellationError
),那么将其包装在某些超时逻辑中就不太困难了。您只需为search
创建一个任务,创建另一个任务,在一定时间后将cancel
搜索任务,无论哪个任务先完成都会取消另一个任务。请参见https://dev59.com/rsXsa4cB1Zd3GeqPlpLx#74712760。 - Rob