我已经使用 Swift Concurrency 2 年了,但不知道如何解决这种情况。
在名为
APIManager
的类中,有一个执行网络调用的execute方法。
当服务器响应 401 时,需要刷新不记名令牌并重试调用。
看起来大概是这样的:
class APIManager {
func execute<R: ResponseProtocol>(request: RequestProtocol, response: R) async throws -> R.Value {
let session = getURLSession()
let urlRequest: URLRequest = request.buildURLRequest()
let (data, urlResponse) = try await session.data(for: urlRequest)
if let httpURLResponse = urlResponse as? HTTPURLResponse,
httpURLResponse.statusCode == 401 {
try await renewBearerToken()
return try await execute(request: request, response: response)
}
return decode(data: data)
}
}
此类只有一个实例(注入到每个服务中)。 任务在应用程序周围随机触发(例如单击按钮),并调用此方法。
我想要实现的是,每当类加载数据时,尤其是刷新令牌时,请确保任何其他并行触发的任务在完成调用之前等待其完成(否则也可能有遇到 401 的风险)。
对于是否以及如何执行有什么想法吗?任务组、演员?
基本思想是,您将保存对不记名令牌的
Task
的引用,并且 await
它:
class APIManager {
private var bearerTask: Task<Void, Error>?
func execute<R: ResponseProtocol>(request: RequestProtocol, response: R) async throws -> R.Value {
_ = try await bearerTask?.value
let session = getURLSession()
let urlRequest: URLRequest = request.buildURLRequest()
let (data, urlResponse) = try await session.data(for: urlRequest)
if let httpURLResponse = urlResponse as? HTTPURLResponse, httpURLResponse.statusCode == 401 {
bearerTask = Task { try await renewBearerToken() }
return try await execute(request: request, response: response)
}
return decode(data: data)
}
}
就个人而言,我会让 API 管理器成为
actor
(或将其隔离到全局参与者)以避免 bearerTask
上的竞争。
但是您可以让多个 API 调用都等待相同的
bearerTask
。