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

SwiftUI如何强制主线程等待getVIN函数执行完成?

解决异步操作等待问题:让getVIN完成后再跳转

你的问题核心是异步操作的执行顺序:getVIN是和Firestore交互的异步函数,它会在后台线程执行,而主线程会继续往下走,导致你还没等getVIN完成就发送了通知跳转页面。既然你不在意UI卡顿,我们可以通过调整代码的执行顺序,让后续逻辑(存UserDefaults、发通知)等到getVIN完成后再执行。

第一步:给getVIN添加完成回调

首先,你的getVIN函数应该是异步的(因为Firestore查询是异步的),所以我们需要给它加一个completion闭包,用来通知调用者它已经执行完成。假设原来的getVIN大概是这样:

func getVIN(email: String) {
    // Firestore查询逻辑
    Firestore.firestore().collection("someCollection").whereField("email", isEqualTo: email).getDocuments { snapshot, error in
        // 处理查询结果,把VIN存到UserDefaults
    }
}

我们把它改成带completion的版本:

func getVIN(email: String, completion: @escaping () -> Void) {
    Firestore.firestore().collection("someCollection").whereField("email", isEqualTo: email).getDocuments { snapshot, error in
        if let error = error {
            print("获取VIN失败:\(error.localizedDescription)")
            completion() // 即使失败也要调用completion,避免主线程一直等待
            return
        }
        // 处理查询结果,提取VIN并存到UserDefaults
        if let doc = snapshot?.documents.first, let vin = doc["vin"] as? String {
            UserDefaults.standard.set(vin, forKey: "vin")
        }
        completion() // 完成后调用completion通知
    }
}

第二步:调整verify函数的执行顺序

现在,在verify里,我们要把原来在getVIN之后的代码放到completion闭包里,这样就会等getVIN完成后再执行:

func verify() {
    if self.email != "" && self.pass != "" {
        Auth.auth().signIn(withEmail: self.email, password: self.pass) { (res, err) in
            if err != nil {
                print(err!.localizedDescription)
                self.error = err!.localizedDescription
                self.alert.toggle()
                return
            }
            print("success")
            
            // 调用带completion的getVIN,在完成后执行后续逻辑
            self.getVIN(email: self.email) {
                // 这些代码会等getVIN完成后再执行
                UserDefaults.standard.set(true, forKey: "status")
                UserDefaults.standard.set(self.email, forKey: "email")
                print(UserDefaults.standard.string(forKey: "email"))
                
                // 发送通知跳转页面
                NotificationCenter.default.post(name: NSNotification.Name("status"), object: nil)
            }
        }
    } else {
        self.error = "The information is wrong"
        self.alert.toggle()
    }
}

为什么不直接用“强制等待”?

你提到想“强制程序等待”,比如用DispatchSemaphore或者阻塞主线程的方式,但这种方式非常不推荐——因为阻塞主线程会导致整个App完全卡死,用户体验极差,甚至可能被系统判定为无响应而杀死。而用completion闭包的方式,既保证了等待逻辑完成,又不会让App完全僵死(即使你不在意卡顿,这种写法也更规范、安全)。

额外提醒:处理异常情况

如果你的getVIN需要传递错误信息,还可以把completion改成带参数的,让代码更健壮:

func getVIN(email: String, completion: @escaping (String?, Error?) -> Void) {
    Firestore.firestore().collection("someCollection").whereField("email", isEqualTo: email).getDocuments { snapshot, error in
        if let error = error {
            completion(nil, error)
            return
        }
        if let doc = snapshot?.documents.first, let vin = doc["vin"] as? String {
            completion(vin, nil)
        } else {
            completion(nil, NSError(domain: "VINError", code: 0, userInfo: [NSLocalizedDescriptionKey: "未找到VIN"]))
        }
    }
}

调用时就能处理成功/失败的情况:

self.getVIN(email: self.email) { vin, error in
    if let error = error {
        print("获取VIN失败:\(error.localizedDescription)")
        self.error = error.localizedDescription
        self.alert.toggle()
        return
    }
    // 成功后执行后续逻辑
    if let vin = vin {
        UserDefaults.standard.set(vin, forKey: "vin")
    }
    UserDefaults.standard.set(true, forKey: "status")
    UserDefaults.standard.set(self.email, forKey: "email")
    NotificationCenter.default.post(name: NSNotification.Name("status"), object: nil)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 12:52:49