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

求助:For循环闭包异步执行导致Firebase用户数组顺序异常

Solution for Preserving User Array Order with Firebase Async Calls

The core issue is that Firebase's getDocument is asynchronous—each request completes whenever Firebase responds, not in the order you initiate them. Your original code appends users as they arrive, leading to mismatched order, and also calls completion early if any user doesn't exist. Here are two fixes:

1. Closure-Based Approach (Compatible with All Swift Versions)

Track the index of each user ID in the original array, pre-allocate an array to hold results, and only call completion once all requests finish:

var usersArray: [FUser?] = Array(repeating: nil, count: withIds.count)
var completedRequests = 0

for (index, userId) in withIds.enumerated() {
    print("step 1")
    print(userId)
    FirebaseReference(.User).document(userId).getDocument { (snapshot, error) in
        print("step 2")
        print(userId)
        guard let snapshot = snapshot else {
            completedRequests += 1
            if completedRequests == withIds.count {
                // Filter out nil entries for non-existent users
                completion(usersArray.compactMap { $0 })
            }
            return
        }
        
        if snapshot.exists {
            let user = FUser(_dictionary: snapshot.data()! as NSDictionary)
            usersArray[index] = user
        }
        
        completedRequests += 1
        
        if completedRequests == withIds.count {
            completion(usersArray.compactMap { $0 })
        }
    }
}

How this works:

  • We initialize usersArray with nil values matching the length of withIds, so each user has a fixed position.
  • Using enumerated() gives us the exact index of each userId in the original array, ensuring we place the fetched user in the correct spot regardless of async completion order.
  • We count every completed request (success or failure) and only call completion once all are done, avoiding early returns.
  • compactMap removes any nil entries for users that don't exist in Firebase, keeping the order of the original withIds array.

2. Async/Await Approach (Swift 5.5+)

If you're using Swift 5.5 or later, async/await simplifies the code to run sequentially, guaranteeing order:

// Mark your function as async
func fetchUsers(withIds: [String], completion: @escaping ([FUser]) -> Void) async {
    var usersArray = [FUser]()
    
    for userId in withIds {
        print("step 1")
        print(userId)
        do {
            let snapshot = try await FirebaseReference(.User).document(userId).getDocument()
            print("step 2")
            print(userId)
            if snapshot.exists {
                let user = FUser(_dictionary: snapshot.data()! as NSDictionary)
                usersArray.append(user)
            }
        } catch {
            print("Failed to fetch user \(userId): \(error.localizedDescription)")
        }
    }
    
    completion(usersArray)
}

How this works:

  • await pauses the loop until each getDocument request completes, ensuring we process users in the exact order of withIds.
  • No need for counters or pre-allocated arrays—users are appended in the correct sequence as each request finishes.
  • Error handling is done with do/catch for each individual request.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 18:14:59