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

Swift调用API时代码无法退出Task的do块问题排查

问题修复方案

你之前猜测self.venues = getInitialVenueInfoWithAllOpenAtDateTimeProperties(…)赋值需要Task块包裹的方向是对的,但仅加Task包裹不足以解决问题,当前执行到第二个API请求的do块就挂起,是两个并发逻辑错误叠加导致的,和Yelp Fusion API本身无关:

  • 主线程被同步阻塞:如果你在主线程(UIViewController的生命周期方法默认跑在主线程)直接调用异步方法,又用了DispatchSemaphore.wait()、Task.result.get()这类同步等待方式强行转同步,会直接卡死主线程,后续第二个API请求的任务恢复逻辑根本得不到调度,自然卡在do块无法继续执行
  • 多请求调度存在隐式阻塞:如果多日期请求的循环逻辑里用了自定义串行队列、非线程安全的共享请求实例,或者自己实现的异步转同步逻辑有死锁,也会导致后续请求永远无法拿到执行资源

修复步骤

1. 修正ViewController层的调用与UI赋值逻辑

绝对不要在主线程同步等待异步网络结果,正确做法是在视图加载时机用Task拉起异步上下文,所有UI属性赋值必须切到MainActor(主线程)执行:

// InitialBusinessResultsViewController.swift 对应调用位置
override func viewDidLoad() {
    super.viewDidLoad()
    // 拉起异步任务,不阻塞主线程
    Task {
        // 网络请求在后台并发上下文执行,不卡住UI
        let fetchedVenues = await getInitialVenueInfoWithAllOpenAtDateTimeProperties(
            // 传入你需要的所有日期时间参数
        )
        // UI相关操作必须切到主线程执行
        await MainActor.run {
            self.venues = fetchedVenues
            // 赋值完成后刷新视图
            self.collectionView.reloadData()
        }
    }
}

2. 重构多日期请求的聚合逻辑,消除手动阻塞

不要用信号量、锁之类的手段手动控制请求顺序,直接用系统提供的withTaskGroup实现并发请求、自动聚合结果,从根源避免死锁:

// GetInitialVenueInfo.swift 内方法实现参考
func getInitialVenueInfoWithAllOpenAtDateTimeProperties(dateTimeList: [TimeInterval]) async -> [Venue] {
    await withTaskGroup(of: [Venue].self) { group in
        var aggregatedVenues = [Venue]()
        // 为每个日期时间参数创建独立的并发请求任务
        for timestamp in dateTimeList {
            group.addTask {
                do {
                    // 这里放单个日期对应的Yelp API请求逻辑
                    let (data, _) = try await URLSession.shared.data(
                        for: buildYelpRequest(openAtTimestamp: timestamp)
                    )
                    let businessResults = try JSONDecoder().decode(YelpSearchResponse.self, from: data)
                    return businessResults.businesses.map { Venue(businessModel: $0, openAt: timestamp) }
                } catch {
                    print("时间戳\(timestamp)对应请求失败: \(error.localizedDescription)")
                    return []
                }
            }
        }
        // 按请求完成顺序聚合所有结果
        for await singleTimeVenues in group {
            aggregatedVenues.append(contentsOf: singleTimeVenues)
        }
        // 按业务要求去重、整理后返回最终结果
        return deduplicateVenues(aggregatedVenues)
    }
}

3. 底层请求逻辑排查点

如果改完上述逻辑仍有挂起,直接检查FetchData.swift的实现:

  • 不要用URLSession.dataTask(with:completionHandler:)套信号量的方式做异步转同步,直接用系统原生的URLSession.data(for:) async接口,没有额外调度开销也不会死锁
  • 挂起时看Xcode左侧导航的线程栈,如果主线程栈顶出现semaphore_wait字样,说明还有残留的同步阻塞主线程的代码,删掉所有同步等待逻辑、统一用async/await写法即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 13:54:04