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
相关产品推荐
相关产品推荐

