Firebase Completion Handler回调执行顺序异常问题排查
问题根源
Firebase 数据库的observe(_:with:)是异步接口:调用该方法发起数据拉取请求后,当前线程不会阻塞等待数据返回,会直接执行后续代码;等网络请求完成、拿到服务端返回的数据后,才会执行传入的snapshot处理闭包。
你当前的代码把completion(arg)写在了observe闭包的外部,会在发起Firebase请求后立刻触发completion回调,根本不会等待数据拉取和解析完成,因此出现执行顺序和预期不符的问题。
修复方案
将completion调用移动到Firebase数据处理闭包的内部,等数组追加、数据打印全部完成后,再触发回调。另外异步执行的闭包需要标记@escaping,否则会触发编译错误。
修正后的method方法代码如下:
func method(arg: Bool, completion: @escaping (Bool) -> ()) { let userID = Auth.auth().currentUser!.uid let database = Database.database().reference() database.child("user/\(userID)/abonniertePoi/").observe(.value, with: { snapshot in for child in snapshot.children.allObjects as! [DataSnapshot] { myFeed.myArray1.append(child.key) } print(myFeed.myArray1) // 所有数据处理完成后再执行回调 completion(arg) }) }
修改后执行顺序会完全符合预期:
- 发起Firebase数据拉取
- 拉取完成后遍历快照、给数组追加元素、打印数组内容
- 执行completion闭包,先打印
Second line of code executed,再根据参数打印true/false
注意事项
- 所有网络请求、IO操作类的接口绝大多数都是异步实现的,不能按照同步代码的线性顺序逻辑来写,依赖异步返回结果的逻辑必须放在对应的回调闭包内执行。
@escaping标记的作用是告诉编译器,这个闭包会在函数返回之后才被调用,需要注意闭包内的对象引用关系,避免循环引用。
内容的提问来源于stack exchange,提问作者submariner
相关产品推荐
相关产品推荐

