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

Swift中为异步网络请求设置2秒超时与3次重试的实现问题

问题分析

原代码的核心问题是在发起异步请求前检查截止时间,此时Date()刚生成deadline,不可能超过设定值,所以超时判断完全无效。要实现真正的异步任务超时,需要让任务在执行过程中监控时间,超过限制时主动终止并抛出错误。

解决方案

根据系统版本,提供两种简洁的实现方式:

方式一:iOS 15+/macOS 12+(推荐)

利用Swift Concurrency原生的withTimeout(_:operation:)API,直接为异步任务设置超时时间,代码更简洁易维护:

enum TaskRetryingError: Error {
    case timedOut
    case exceededMaxRetryCount
    case networkError(Error)
}

func getDetails() async {
    let maxAttempts = 3 // 总共尝试3次
    let timeoutSeconds = 2.0
    
    for attempt in 0..<maxAttempts {
        do {
            // 为当前请求设置2秒超时限制
            let response = try await withTimeout(timeoutSeconds) {
                try await networkCall()
            }
            
            // 请求成功,更新数据并退出循环
            await MainActor.run {
                self.data = response
                self.isSuccess = true
            }
            break
        } catch is CancellationError {
            // 超时会触发CancellationError,转为自定义超时错误
            if attempt == maxAttempts - 1 {
                // 最后一次尝试仍超时,终止并处理错误
                // 这里可添加具体错误逻辑,比如弹窗提示
            }
            // 未到最大次数,继续重试
        } catch {
            // 处理其他网络错误
            if attempt == maxAttempts - 1 {
                // 最后一次仍失败,终止并处理错误
            }
            // 重试
        }
    }
}

方式二:兼容iOS 14及以下版本

如果需要支持更低版本,可以通过同时启动请求任务和超时任务,利用Task.select判断谁先完成:

enum TaskRetryingError: Error {
    case timedOut
    case exceededMaxRetryCount
    case networkError(Error)
}

func getDetails() async {
    let maxAttempts = 3
    let timeoutNanoseconds = 2 * 1_000_000_000 // 2秒转为纳秒
    
    for attempt in 0..<maxAttempts {
        do {
            let requestTask = Task { () -> ResponseType in
                try await networkCall()
            }
            
            let timeoutTask = Task {
                try await Task.sleep(nanoseconds: timeoutNanoseconds)
                throw TaskRetryingError.timedOut
            }
            
            // 等待先完成的任务
            let response = try await Task.select(requestTask, timeoutTask).value
            
            // 取消未完成的任务,避免资源浪费
            if !requestTask.isCancelled {
                timeoutTask.cancel()
            }
            
            await MainActor.run {
                self.data = response
                self.isSuccess = true
            }
            break
        } catch TaskRetryingError.timedOut {
            if attempt == maxAttempts - 1 {
                // 最后一次超时,终止并处理错误
            }
        } catch {
            if attempt == maxAttempts - 1 {
                // 最后一次仍失败,终止并处理错误
            }
        }
    }
}
关键逻辑说明
  • 每次重试时为当前请求单独设置超时,确保每一次尝试都严格遵循2秒时间限制
  • 当达到最大尝试次数(3次)时,不再重试,直接处理最终错误
  • 使用MainActor.run确保UI更新操作在主线程执行,避免线程安全问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 12:45:37