截止日期

Swift 并发机制的截止日期算法。

理由

正如我之前在 Swift 论坛 上所述:在我看来,截止日期或超时是 Swift 并发系统中缺失的一部分。由于这个算法不容易实现正确,我决定开源我的实现。

详情

该库附带两个自由函数,一个带有通用时钟,另一个使用 ContinuousClock 作为默认时钟。

public func deadline<C, R>(
  until instant: C.Instant,
  tolerance: C.Instant.Duration? = nil,
  clock: C,
  isolation: isolated (any Actor)? = #isolation,
  operation: @Sendable () async throws -> R
) async throws -> R where C: Clock, R: Sendable { ... }

public func deadline<R>(
  until instant: ContinuousClock.Instant,
  tolerance: ContinuousClock.Instant.Duration? = nil,
  isolation: isolated (any Actor)? = #isolation,
  operation: @Sendable () async throws -> R
) async throws -> R where R: Sendable { ... }

此函数提供了一种机制,用于对缺乏原生截止日期支持的异步操作强制执行超时。它创建一个包含两个并发任务的 TaskGroup:提供的操作和一个休眠任务。

注意

操作闭包必须支持协作取消。否则,截止日期将不会被遵守。

示例

为了充分理解这一点,让我们说明此函数的 3 种结果

结果 1

操作及时完成

let result = try await deadline(until: .now + .seconds(5)) {
  // Simulate long running task
  try await Task.sleep(for: .seconds(1))
  return "success"
}

正如您所预料的那样,结果将是“success”。当您的操作及时失败时,情况也是如此

let result = try await deadline(until: .now + .seconds(5)) {
  // Simulate long running task
  try await Task.sleep(for: .seconds(1))
  throw CustomError()
}

这将抛出 CustomError

结果 2

操作未能在规定时间内完成

let result = try await deadline(until: .now + .seconds(1)) {
  // Simulate even longer running task
  try await Task.sleep(for: .seconds(5))
  return "success"
}

这将抛出 DeadlineExceededError,因为操作将无法在规定时间内完成。

结果 3

父任务被取消

let task = Task {
  do {
    try await deadline(until: .now + .seconds(5)) {
      try await URLSession.shared.data(from: url)
    }
  } catch {
    print(error)
  }
}

task.cancel()

打印保证会打印 URLError(.cancelled)

改进