求助: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
usersArraywithnilvalues matching the length ofwithIds, so each user has a fixed position. - Using
enumerated()gives us the exact index of eachuserIdin 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
completiononce all are done, avoiding early returns. compactMapremoves anynilentries for users that don't exist in Firebase, keeping the order of the originalwithIdsarray.
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:
awaitpauses the loop until eachgetDocumentrequest completes, ensuring we process users in the exact order ofwithIds.- 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/catchfor each individual request.
内容的提问来源于stack exchange,提问作者Dani
相关产品推荐
相关产品推荐

