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

如何确保所有网络请求完成后再访问Core Data并跳转视图控制器?

解决异步API请求未完成就跳转导致Core Data为空的问题

你猜的完全没错!问题就出在这些异步API请求是并行执行的——apiCallFive可能在apiCallOne到apiCallFour完成前就触发了跳转,导致ViewController B加载时,Core Data还没拿到前面请求的数据,自然显示为空。

要实现所有请求完成后再跳转,最常用也最简洁的方案是用DispatchGroup来管理所有异步任务,下面是具体实现:

方案一:用DispatchGroup等待所有前置请求完成

我们可以用DispatchGroup的enter()和leave()来标记每个任务的开始和结束,等所有任务都完成后,再执行apiCallFive和跳转逻辑:

Api.verifyOtp(email, otp).continueWith { (task) -> Any? in 
    if task.succeed { 
        // 创建一个DispatchGroup实例
        let taskGroup = DispatchGroup()
        
        // 给每个前置请求标记任务开始/结束
        taskGroup.enter()
        self.apiCallOne().continueWith { _ in
            // 请求完成后标记任务结束
            taskGroup.leave()
        }
        
        taskGroup.enter()
        self.apiCallTwo().continueWith { _ in
            taskGroup.leave()
        }
        
        taskGroup.enter()
        self.apiCallThree().continueWith { _ in
            taskGroup.leave()
        }
        
        taskGroup.enter()
        self.apiCallFour().continueWith { _ in
            taskGroup.leave()
        }
        
        // 等待所有前置任务完成后,在主队列执行后续逻辑(跳转UI必须在主队列)
        taskGroup.notify(queue: .main) {
            // 现在执行apiCallFive,完成后跳转
            Api.apiCallFive().continueOnSuccessWith { (task) -> Any? in 
                Hud.hide() 
                if task.succeed { 
                    let storyboard = UIStoryboard(name: "Main", bundle: nil) 
                    let viewB = storyboard.instantiateViewController(withIdentifier: "ViewControllerB")
                    self.navigationController?.pushViewController(viewB, animated: true) 
                }
                return nil
            }
        }
    } else { 
        Hud.hide() 
        task.showError() 
    }
    return nil 
}

关键说明:

  • enter()和leave()必须成对出现:每调用一次enter(),就必须在任务完成(无论成功或失败)时调用一次leave(),否则notify永远不会触发。
  • notify(queue: .main)确保跳转逻辑在主队列执行,符合iOS UI操作的线程要求。
  • 如果需要确保所有前置请求都成功才执行跳转,可以在每个continueWith里判断任务状态,失败时处理错误并调用leave(),比如:
    self.apiCallOne().continueWith { task in
        if !task.succeed {
            task.showError()
        }
        taskGroup.leave()
    }
    

方案二:用OperationQueue管理任务依赖

如果需要更精细的任务控制(比如设置并行数、取消任务等),可以用OperationQueue给任务添加依赖,让apiCallFive的跳转任务必须等待前四个请求完成:

Api.verifyOtp(email, otp).continueWith { (task) -> Any? in 
    if task.succeed { 
        let operationQueue = OperationQueue()
        operationQueue.maxConcurrentOperationCount = 4 // 允许4个任务并行
        
        // 封装每个API请求为BlockOperation(注意用信号量把异步转同步)
        let op1 = BlockOperation {
            let semaphore = DispatchSemaphore(value: 0)
            self.apiCallOne().continueWith { _ in
                semaphore.signal()
            }
            semaphore.wait()
        }
        
        let op2 = BlockOperation {
            let semaphore = DispatchSemaphore(value: 0)
            self.apiCallTwo().continueWith { _ in
                semaphore.signal()
            }
            semaphore.wait()
        }
        
        let op3 = BlockOperation {
            let semaphore = DispatchSemaphore(value: 0)
            self.apiCallThree().continueWith { _ in
                semaphore.signal()
            }
            semaphore.wait()
        }
        
        let op4 = BlockOperation {
            let semaphore = DispatchSemaphore(value: 0)
            self.apiCallFour().continueWith { _ in
                semaphore.signal()
            }
            semaphore.wait()
        }
        
        // 封装跳转任务(必须在主队列执行UI操作)
        let jumpOp = BlockOperation {
            DispatchQueue.main.async {
                Api.apiCallFive().continueOnSuccessWith { (task) -> Any? in 
                    Hud.hide() 
                    if task.succeed { 
                        let storyboard = UIStoryboard(name: "Main", bundle: nil) 
                        let viewB = storyboard.instantiateViewController(withIdentifier: "ViewControllerB")
                        self.navigationController?.pushViewController(viewB, animated: true) 
                    }
                    return nil
                }
            }
        }
        
        // 设置跳转任务依赖于前四个请求任务
        jumpOp.addDependency(op1)
        jumpOp.addDependency(op2)
        jumpOp.addDependency(op3)
        jumpOp.addDependency(op4)
        
        // 添加任务到队列执行
        operationQueue.addOperations([op1, op2, op3, op4], waitUntilFinished: false)
        OperationQueue.main.addOperation(jumpOp)
    } else { 
        Hud.hide() 
        task.showError() 
    }
    return nil 
}

额外注意事项

  • 确保Core Data的写入操作线程安全:如果API回调在后台队列,要使用对应的Core Data上下文,避免直接在后台队列操作主上下文,否则可能导致数据写入延迟或崩溃。
  • 如果你的API框架支持Promise风格的链式调用(比如PromiseKit),也可以用when(fulfilled:)来组合多个异步任务,写法会更简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:23:40