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

Swift:如何在主线程等待dispatch_async执行完成?

问题描述

我现在遇到个棘手的问题,明明感觉答案就在嘴边却想不出来。我在用FirebaseAuth库,没法修改它(也不想改)。getIDTokenForcingRefresh()用的是dispatch_async异步回调,如果它支持Swift 5.5的async/await就简单多了,但现在只能靠基于dispatch_async的方案解决。我需要拿到返回的token来调用Firebase函数接口,但下面的代码明显不行——函数会在getIDTokenForcingRefresh()执行完成前就返回空token。我不介意主线程被阻塞,因为用户完成这个操作前本来也没法继续用APP。

问题代码:

var userToken: String {
    print("FIREBASE: Getting User Token")
    var token = ""
    Auth.auth().currentUser?.getIDTokenForcingRefresh(true) { idToken, error in
        if let error = error {
            print("FIREBASE: There was an error getting the user token")
            return
        }
        print("FIREBASE: Got user token: \(idToken ?? "NONE")")
        token = idToken ?? ""
    }
    print("FIREBASE: Token: \(token)")
    return token
}
解决方案

既然你不介意阻塞主线程,可以用DispatchSemaphore来同步等待异步回调完成,修改后的代码如下:

var userToken: String {
    print("FIREBASE: Getting User Token")
    var token = ""
    let semaphore = DispatchSemaphore(value: 0)
    
    Auth.auth().currentUser?.getIDTokenForcingRefresh(true) { idToken, error in
        defer {
            semaphore.signal() // 不管成功失败都发送信号,解除线程阻塞
        }
        if let error = error {
            print("FIREBASE: 获取用户token出错: \(error.localizedDescription)")
            return
        }
        print("FIREBASE: 获取到用户token: \(idToken ?? "NONE")")
        token = idToken ?? ""
    }
    
    semaphore.wait() // 阻塞当前线程,直到回调中发送信号
    print("FIREBASE: Token: \(token)")
    return token
}

关键说明

  • DispatchSemaphore的wait()方法会卡住当前主线程,直到回调里调用signal(),确保token被赋值后再返回结果。
  • 用defer语句保证即便回调出错,也一定会触发signal(),避免线程永久阻塞。
  • 虽然你接受主线程阻塞,但要注意:如果getIDTokenForcingRefresh()回调耗时过长,可能导致APP界面卡顿甚至被系统终止,尽量确保该操作耗时在合理范围内。

如果之后想迁移到async/await写法,也可以把这个异步方法封装成async函数,示例如下:

func getUserToken() async throws -> String {
    return try await withCheckedThrowingContinuation { continuation in
        Auth.auth().currentUser?.getIDTokenForcingRefresh(true) { idToken, error in
            if let error = error {
                continuation.resume(throwing: error)
                return
            }
            guard let token = idToken else {
                continuation.resume(throwing: NSError(domain: "AuthError", code: -1, userInfo: [NSLocalizedDescriptionKey: "未获取到有效token"]))
                return
            }
            continuation.resume(returning: token)
        }
    }
}

之后就能在async上下文里用let token = try await getUserToken()获取token,无需阻塞线程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 13:17:21