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

Swift环境下如何实现两个HTTP请求的同步有序执行?寻求标准优化方案

解决Swift中顺序执行HTTP请求的最优方案

嘿,我来帮你搞定这个问题~你现在用循环+标记变量的方式实现顺序请求,确实存在代码冗余、容易出bug的问题,而且效率不高。先逐个分析你提到的几个方案:

  • 循环+标记变量:这种方式不仅代码啰嗦,还容易因为标记变量的状态管理(比如忘记重置)导致逻辑错误,而且空循环等待会浪费CPU资源,完全不推荐。
  • DispatchSemaphore:如果在主线程调用semaphore.wait(),确实会阻塞UI线程,导致界面卡顿;就算在后台线程使用,也会让异步代码变得更复杂,不符合Swift现代异步编程的趋势,没必要用。
  • NSLock:它主要用于多线程环境下的资源互斥访问,不是用来处理异步任务顺序执行的工具,用在这里属于误用,会增加不必要的复杂度。

最优方案:正确利用Swift的Async/Await

你已经把函数声明为async了,但里面还是用了传统的dataTask回调,这等于没发挥Async/Await的优势。Swift的URLSession已经提供了异步版本的API(data(for:)),用它可以让你的代码逻辑更清晰,天然支持顺序执行,完全不需要标记变量或者同步锁。

第一步:改造请求函数为纯Async/Await风格

先把你的doHTTPRecentProjectsCall改成异步抛出错误的版本,去掉回调:

func doHTTPRecentProjectsCall(employeeId: String) async throws { 
    let dateFormatter = DateFormatter() 
    dateFormatter.dateFormat = "yyyyMMdd" 
    let todaysDate = dateFormatter.string(from: Date()) 
    
    // 用字符串插值简化URL拼接,更易读
    guard let reqUrl = URL(string: "http://192.168.179.185:8160/api/v1/project/recent/\(employeeId)?date=\(todaysDate)") else { 
        throw NSError(domain: "InvalidURL", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])
    } 
    
    var req = URLRequest(url: reqUrl) 
    req.httpMethod = "GET" 
    req.setValue("CC0001", forHTTPHeaderField: "CC-Tenant") 
    req.setValue("BE", forHTTPHeaderField: "CC-Product") 
    
    let formatter = DateFormatter() 
    formatter.dateFormat = "yyyy-MM-dd" 
    formatter.timeZone = TimeZone(abbreviation: "UTC") // 这里应该是UTC?你之前写的ETC可能是笔误
    formatter.locale = Locale(identifier: "en_US_POSIX") // 避免地区格式差异
    
    // 使用异步版本的data(for:),自动等待请求完成
    let (data, response) = try await URLSession.shared.data(for: req)
    
    // 可选:处理HTTP状态码,比如非200系列抛出错误
    if let httpResponse = response as? HTTPURLResponse, !(200...299).contains(httpResponse.statusCode) {
        throw NSError(domain: "HTTPError", code: httpResponse.statusCode, userInfo: [NSLocalizedDescriptionKey: "Request failed with status code \(httpResponse.statusCode)"])
    }
    
    let decoder = JSONDecoder() 
    decoder.dateDecodingStrategy = .formatted(formatter) 
    recentProjects = try decoder.decode(Array<Project>.self, from: data) 
    
    // 清空旧数据,避免残留
    recentProjectsDic.removeAll()
    recent.removeAll()
    for val in recentProjects { 
        recentProjectsDic[val.name] = val 
        recent.append(val.name) 
    } 
    print("test1") 
}

用同样的方式改造你的第二个请求doHTTPProjectsCall,让它也变成async throws函数。

第二步:顺序调用请求

现在你可以直接按顺序调用两个函数,await会自动等待前一个请求完成后再执行下一个,完全不需要标记变量:

// 用Task包裹异步逻辑,避免在同步上下文直接调用await
Task {
    while !Task.isCancelled && projects.isEmpty { 
        do {
            // 先执行第一个请求,等待完成
            try await doHTTPRecentProjectsCall(employeeId: self.employeeId) 
            // 第一个请求完成后,自动执行第二个
            try await doHTTPProjectsCall(token: apiKey, employeeId: self.employeeId) 
        } catch {
            // 统一处理请求错误
            print("Request failed: \(error.localizedDescription)")
        }
        
        if projects.isEmpty { 
            do{ 
                try await Task.sleep(nanoseconds: 2_000_000_000) 
            } catch { 
                print("Sleep interrupted: \(error)") 
            } 
        } 
    }
}

为什么这是最优解?

  1. 代码简洁清晰:去掉了回调嵌套和标记变量,逻辑一目了然,可读性极强。
  2. 天然支持顺序执行:await关键字会暂停当前异步任务,等待前一个请求完成后再继续,完全符合你的需求。
  3. 规范的错误处理:用throws和do/catch统一处理所有错误(URL无效、请求失败、解析错误等),避免遗漏异常情况。
  4. 非阻塞主线程:Async/Await基于Swift的结构化并发模型,会自动管理线程,不会阻塞主线程,保证界面流畅。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 20:32:35