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

Swift异步网络请求:如何在出错时实现重试机制?

异步网络请求添加自动重试逻辑(无需修改外部Do Catch)

核心思路

  1. 抽离核心请求逻辑到内部函数,便于复用和重试调用
  2. 仅针对特定可重试错误(如Token过期)触发重试,其他错误直接抛出
  3. 内置Token刷新逻辑,重试时自动使用新Token
  4. 设置默认重试次数,外部调用无需修改原有代码

修改后的完整代码

首先补充NetworkError枚举值,用于区分可重试错误:

enum NetworkError: Error {
    case badURL
    case invalidToken
    case invalidResponse
    case decodingError
    case tokenExpired
    case tokenRefreshFailed
    case maxRetriesExceeded
}

然后改造request函数,加入重试和Token刷新逻辑:

func request<T: Codable>(_ endpoint: Endpoint, retryCount: Int = 3) async throws -> T {
    var attempt = 0
    while attempt < retryCount {
        do {
            return try await performCoreRequest(endpoint: endpoint)
        } catch NetworkError.tokenExpired {
            // Token过期,尝试刷新并重试
            attempt += 1
            if attempt >= retryCount {
                throw NetworkError.tokenRefreshFailed
            }
            try await refreshToken()
        } catch {
            // 非重试类错误直接抛出,不改变原有错误处理逻辑
            throw error
        }
    }
    throw NetworkError.maxRetriesExceeded
}

// 核心请求逻辑抽离
private func performCoreRequest<T: Codable>(endpoint: Endpoint) async throws -> T {
    guard let url = endpoint.url else {
        throw NetworkError.badURL
    }
    
    guard let token = idToken else {
        throw NetworkError.invalidToken
    }
    
    let request = buildRequest(from: url, methodType: endpoint.methodType, idToken: token)
    let (data, response) = try await URLSession.shared.data(for: request)
    
    guard let httpResponse = response as? HTTPURLResponse else {
        throw NetworkError.invalidResponse
    }
    
    // 识别Token过期(通常对应401状态码)
    if httpResponse.statusCode == 401 {
        throw NetworkError.tokenExpired
    }
    
    guard httpResponse.statusCode == 200 else {
        throw NetworkError.invalidResponse
    }
    
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    do {
        let result = try decoder.decode(T.self, from: data)
        print("Result: \(result)")
        return result
    } catch {
        throw NetworkError.decodingError
    }
}

// Token刷新逻辑(根据实际业务实现)
private func refreshToken() async throws {
    // 构造刷新Token的请求
    guard let refreshUrl = URL(string: "https://your-api-domain.com/refresh-token") else {
        throw NetworkError.badURL
    }
    let refreshEndpoint = Endpoint(url: refreshUrl, methodType: .post)
    
    // 刷新请求不重试,避免循环调用
    let tokenResponse: TokenResponse = try await request(refreshEndpoint, retryCount: 0)
    // 更新全局Token
    idToken = tokenResponse.accessToken
}

// 示例Token响应模型
struct TokenResponse: Codable {
    let accessToken: String
}

关键说明

  • 无需修改外部调用:默认重试次数设为3,外部调用request(endpoint)时无需额外参数,原有Do Catch逻辑完全兼容
  • 精准重试控制:仅对tokenExpired错误触发重试,其他错误(如URL无效、解码失败)直接抛出,不干扰原有错误处理
  • 避免重复刷新:刷新Token时调用request传入retryCount:0,防止刷新请求本身触发重试导致循环
  • 线程安全优化:如果需要处理多请求同时触发Token刷新的场景,可以添加异步锁(如Actor)避免重复请求刷新接口

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 14:50:16