You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何暂停多个async/await调用直至单次网络请求返回?

用Async/Await实现单次Token刷新的方案

核心思路

完全可以用类似Completion Handler的思路实现,而且必须用到Continuations——它能把回调式的网络请求转换成可等待的async函数,同时让多个等待的任务共享同一次刷新请求,避免重复调用后端接口。

具体实现步骤

  • 维护两个关键状态变量:
    • isRefreshingToken: 布尔值,标记当前是否正在执行token刷新操作
    • pendingContinuations: 数组,存储所有等待token刷新完成的continuation闭包
  • 当任务因token过期失败时:
    1. 检查isRefreshingToken状态:
      • 如果为false:将其设为true,发起token刷新网络请求。请求完成后,遍历pendingContinuations用新token(或错误信息)唤醒所有等待的任务,最后清空数组并把isRefreshingToken重置为false
      • 如果为true:把当前任务的continuation加入pendingContinuations队列,等待刷新完成后统一处理

代码示例(Swift)

class TokenManager {
    private var currentToken: String?
    private var isRefreshingToken = false
    private var pendingContinuations: [(Result<String, Error>) -> Void] = []
    private let lock = NSLock() // 多线程环境下保证状态安全

    func getValidToken() async throws -> String {
        lock.lock()
        let hasValidToken = currentToken != nil && !isTokenExpired(currentToken!)
        lock.unlock()
        
        if hasValidToken {
            return currentToken!
        }

        return try await withCheckedThrowingContinuation { continuation in
            lock.lock()
            defer { lock.unlock() }
            
            if isRefreshingToken {
                // 已有刷新请求,加入等待队列
                pendingContinuations.append(continuation.resume(with:))
            } else {
                isRefreshingToken = true
                // 发起实际的token刷新请求
                refreshToken { [weak self] result in
                    guard let self = self else { return }
                    
                    self.lock.lock()
                    defer { self.lock.unlock() }
                    
                    self.isRefreshingToken = false
                    // 处理刷新结果并通知所有等待任务
                    switch result {
                    case .success(let newToken):
                        self.currentToken = newToken
                        self.pendingContinuations.forEach { $0(.success(newToken)) }
                    case .failure(let error):
                        self.pendingContinuations.forEach { $0(.failure(error)) }
                    }
                    self.pendingContinuations.removeAll()
                    
                    // 唤醒当前发起请求的任务
                    continuation.resume(with: result)
                }
            }
        }
    }

    // 判断token是否过期的逻辑
    private func isTokenExpired(_ token: String) -> Bool {
        // 这里替换成实际的过期校验逻辑
        return true
    }

    // 模拟后端刷新token的网络请求
    private func refreshToken(completion: @escaping (Result<String, Error>) -> Void) {
        DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
            completion(.success("new_refreshed_token_2024"))
        }
    }
}

关键细节

  • withCheckedThrowingContinuation是核心:它能让async函数暂停执行,直到我们手动调用resume方法唤醒它,完美适配回调式的网络请求
  • 线程安全不可少:如果是多线程环境,必须给状态变量加锁(比如示例里的NSLock),避免并发访问导致数组越界或状态混乱
  • 错误统一处理:刷新失败时,所有等待的任务都会收到同一个错误,符合业务逻辑的一致性

内容的提问来源于stack exchange,提问作者Hackerman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 12:05:11