等待swift循环,asynchronousnetworking请求完成执行
我想要一个for循环发送一堆networking请求到firebase,然后传递数据到一个新的视图控制器,一旦方法完成执行。 这是我的代码:
var datesArray = [String: AnyObject]() for key in locationsArray { let ref = Firebase(url: "http://myfirebase.com/" + "\(key.0)") ref.observeSingleEventOfType(.Value, withBlock: { snapshot in datesArray["\(key.0)"] = snapshot.value }) } //Segue to new view controller here, and pass datesArray once it is complete
我有几个担心。 首先,如何等待for循环完成并且所有networking请求都完成? 我无法修改observeSingleEventOfType函数,它是firebase SDK的一部分。 另外,我会通过尝试访问来自for循环的不同迭代的datesArray来创build某种竞争条件(希望是有道理的)? 我一直在阅读关于GCD和NSOperation,但我有点失落,因为这是我build立的第一个应用程序。
注意:Locations数组是包含我需要在Firebase中访问的键的数组。 此外,networking请求asynchronous触发也很重要。 我只想等到所有的asynchronous请求完成,然后再将datesArray传递给下一个视图控制器。
当所有请求完成时,您可以使用分派组来触发asynchronouscallback。
下面是Swift 3中的一个例子,当多个联网请求全部完成时,使用调度组asynchronous执行callback。
override func viewDidLoad() { super.viewDidLoad() let myGroup = DispatchGroup() for i in 0 ..< 5 { myGroup.enter() Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in print("Finished request \(i)") myGroup.leave() } } myGroup.notify(queue: .main) { print("Finished all requests.") } }
产量
Finished request 1 Finished request 0 Finished request 2 Finished request 3 Finished request 4 Finished all requests.
对于那些使用较老的Swift 2.3的人来说,下面是一个使用它的语法的例子:
override func viewDidLoad() { super.viewDidLoad() let myGroup = dispatch_group_create() for i in 0 ..< 5 { dispatch_group_enter(myGroup) Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in print("Finished request \(i)") dispatch_group_leave(self.myGroup) } } dispatch_group_notify(myGroup, dispatch_get_main_queue(), { print("Finished all requests.") }) }
Xcode 8.3.1 – Swift 3
这是保罗斯接受的答案,转换为斯威夫特3:
let myGroup = DispatchGroup() override func viewDidLoad() { super.viewDidLoad() for i in 0 ..< 5 { myGroup.enter() Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in print("Finished request \(i)") myGroup.leave() } } myGroup.notify(queue: DispatchQueue.main, execute: { print("Finished all requests.") }) }
你将需要为此使用信号量。
//Create the semaphore with count equal to the number of requests that will be made. let semaphore = dispatch_semaphore_create(locationsArray.count) for key in locationsArray { let ref = Firebase(url: "http://myfirebase.com/" + "\(key.0)") ref.observeSingleEventOfType(.Value, withBlock: { snapshot in datesArray["\(key.0)"] = snapshot.value //For each request completed, signal the semaphore dispatch_semaphore_signal(semaphore) }) } //Wait on the semaphore until all requests are completed let timeoutLengthInNanoSeconds: Int64 = 10000000000 //Adjust the timeout to suit your case let timeout = dispatch_time(DISPATCH_TIME_NOW, timeoutLengthInNanoSeconds) dispatch_semaphore_wait(semaphore, timeout) //When you reach here all request would have been completed or timeout would have occurred.
Swift 3:你也可以用这种方式使用信号量。 它的结果非常有帮助,除此之外,您还可以确切了解何时完成了哪些stream程。 这已经从我的代码中提取:
//You have to create your own queue or if you need the Default queue let persons = persistentContainer.viewContext.persons print("How many persons on database: \(persons.count())") let numberOfPersons = persons.count() for eachPerson in persons{ queuePersonDetail.async { self.getPersonDetailAndSave(personId: eachPerson.personId){person2, error in print("Person detail: \(person2?.fullName)") //When we get the completionHandler we send the signal semaphorePersonDetailAndSave.signal() } } } //Here we will wait for i in 0..<numberOfPersons{ semaphorePersonDetailAndSave.wait() NSLog("\(i + 1)/\(persons.count()) completed") } //And here the flow continues...
派遣组是好的,但发送请求的顺序是随机的。
Finished request 1 Finished request 0 Finished request 2
在我的项目案例中,每个需要启动的请求都是正确的顺序。 如果这可以帮助某人:
public class RequestItem: NSObject { public var urlToCall: String = "" public var method: HTTPMethod = .get public var params: [String: String] = [:] public var headers: [String: String] = [:] } public func trySendRequestsNotSent (trySendRequestsNotSentCompletionHandler: @escaping ([Error]) -> () = { _ in }) { // If there is requests if !requestItemsToSend.isEmpty { let requestItemsToSendCopy = requestItemsToSend NSLog("Send list started") launchRequestsInOrder(requestItemsToSendCopy, 0, [], launchRequestsInOrderCompletionBlock: { index, errors in trySendRequestsNotSentCompletionHandler(errors) }) } else { trySendRequestsNotSentCompletionHandler([]) } } private func launchRequestsInOrder (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], launchRequestsInOrderCompletionBlock: @escaping (_ index: Int, _ errors: [Error] ) -> Void) { executeRequest(requestItemsToSend, index, errors, executeRequestCompletionBlock: { currentIndex, errors in if currentIndex < requestItemsToSend.count { // We didn't reach last request, launch next request self.launchRequestsInOrder(requestItemsToSend, currentIndex, errors, launchRequestsInOrderCompletionBlock: { index, errors in launchRequestsInOrderCompletionBlock(currentIndex, errors) }) } else { // We parse and send all requests NSLog("Send list finished") launchRequestsInOrderCompletionBlock(currentIndex, errors) } }) } private func executeRequest (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], executeRequestCompletionBlock: @escaping (_ index: Int, _ errors: [Error]) -> Void) { NSLog("Send request %d", index) Alamofire.request(requestItemsToSend[index].urlToCall, method: requestItemsToSend[index].method, parameters: requestItemsToSend[index].params, headers: requestItemsToSend[index].headers).responseJSON { response in var errors: [Error] = errors switch response.result { case .success: // Request sended successfully, we can remove it from not sended request array self.requestItemsToSend.remove(at: index) break case .failure: // Still not send we append arror errors.append(response.result.error!) break } NSLog("Receive request %d", index) executeRequestCompletionBlock(index+1, errors) } }
致电:
trySendRequestsNotSent()
结果:
Send list started Send request 0 Receive request 0 Send request 1 Receive request 1 Send request 2 Receive request 2 ... Send list finished
查看更多信息: Gist
斯威夫特3或4
如果你不关心订单 ,使用@ paulvs的答案 ,它完美的作品。
否则,以防万一任何人想要得到结果,而不是同时发射它们, 这里是代码。
let dispatchGroup = DispatchGroup() let dispatchQueue = DispatchQueue(label: "taskQueue") let dispatchSemaphore = DispatchSemaphore(value: 0) dispatchQueue.async { // use array categories as an example. for c in self.categories { if let id = c.categoryId { dispatchGroup.enter() self.downloadProductsByCategory(categoryId: id) { success, data in if success, let products = data { self.products.append(products) } dispatchSemaphore.signal() dispatchGroup.leave() } dispatchSemaphore.wait() } } } dispatchGroup.notify(queue: dispatchQueue) { DispatchQueue.main.async { self.refreshOrderTable { _ in self.productCollectionView.reloadData() } } }