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
相关产品推荐
相关产品推荐

