在加载数据之前完成所有asynchronous请求?

我遇到了一个问题,我有多个asynchronous请求,从Facebook API和我的Firebase数据库抓取图像和信息。 我想执行所有的asynchronous请求,然后将从Facebook API / Firebase数据库中获取的所有数据存储到一个可以快速加载的完整对象中。 我已经为每个asynchronous请求设置了完成处理程序,我认为强制程序在请求完成之前“等待”,然后让程序继续运行,但这似乎不适用于我。 以下是我的尝试:

func setupEvents(completion: (result: Bool, Event: Event) -> Void){ // Get a reference to Events eventsReference = Firebase(url:"<DB Name>") eventAttendeesRef = Firebase(url:"<DB Name>") //Read the data at our posts reference println("Event References: \(eventsReference)") eventsReference.observeEventType(FEventType.ChildAdded, withBlock: { (snapshot) -> Void in let eventName = snapshot.value["eventName"] as? String let eventLocation = snapshot.value["eventLocation"] as? String let eventCreator = snapshot.value["eventCreator"] as? String var attendees: NSMutableDictionary = [:] var attendeesImages = [UIImage]() let attendee: NSMutableDictionary = [:] let group = dispatch_group_create() //Get attendees first dispatch_group_enter(group) self.getAttendees(snapshot.key as String, completion:{ (result, name, objectID) -> Void in if(result == true){ println("Finished grabbing \(name!) \(objectID!)") attendees.addEntriesFromDictionary(attendee as [NSObject : AnyObject]) } else { println("False") } dispatch_group_leave(group) }) //Get attendees photos dispatch_group_enter(group) self.getAttendeesPictures(attendee, completion: { (result, image) -> Void in if result == true { println("Finished getting attendee photos. Now to store into Event object.") attendeesImages.append(image!) } else{ println("false") } dispatch_group_leave(group) }) dispatch_group_notify(group, dispatch_get_main_queue()) { println("both requests done") //Maintain array snapshot keys self.eventIDs.append(snapshot.key) if snapshot != nil { let event = Event(eventName: eventName, eventLocation:eventLocation, eventPhoto:eventPhoto, fromDate:fromDate, fromTime:fromTime, toDate:toDate, toTime:toTime, attendees: attendees, attendeesImages:attendeesImages, attendeesImagesTest: attendeesImagesTest, privacy:privacy, eventCreator: eventCreator, eventCreatorID: eventCreatorID) println("Event: \(event)") completion(result: true, Event: event) } } }) { (error) -> Void in println(error.description) } } 

我知道我有我的完成处理程序正确设置,因为我已经在我的程序中testing过。 但是,我想要的是,只有在getAttendeesgetAttendeesPictures函数完成后,我才能存储抓取snapshotgetAttendeesgetAttendeesPictures函数的所有信息,并将它们存储到event对象中。 任何想法如何做到这一点? 我试图调查dispatch_groups来帮助我通过这个链接来处理: 检查来自Alamofire和Swift的多个asynchronous响应,但是我的程序似乎只执行getAttendees函数,而不执行getAttendeesPictures函数。 下面是getAttendeesgetAttendeesPictures函数:

 func getAttendees(child: String, completion: (result: Bool, name: String?, objectID: String?) -> Void){ //Get event attendees of particular event var attendeesReference = self.eventAttendeesRef.childByAppendingPath(child) println("Loading event attendees") //Get all event attendees attendeesReference.observeEventType(FEventType.ChildAdded, withBlock: { (snapshot) -> Void in let name = snapshot.value.objectForKey("name") as? String let objectID = snapshot.value.objectForKey("objectID") as? String println("Name: \(name) Object ID: \(objectID)") completion(result: true, name: name, objectID: objectID) }) { (error) -> Void in println(error.description) } func getAttendeesPictures(attendees: NSMutableDictionary, completion: (result: Bool, image: UIImage?)-> Void){ println("Attendees Count: \(attendees.count)") for (key, value) in attendees{ let url = NSURL(string: "https://graph.facebook.com/\(key)/picture?type=large") println("URL: \(url)") let urlRequest = NSURLRequest(URL: url!) //Asynchronous request to display image NSURLConnection.sendAsynchronousRequest(urlRequest, queue: NSOperationQueue.mainQueue()) { (response:NSURLResponse!, data:NSData!, error:NSError!) -> Void in if error != nil{ println("Error: \(error)") } // Display the image let image = UIImage(data: data) if(image != nil){ completion(result: true, image: image) } } } } 

对于在标题中寻求问题答案的用户,则使用dispatch_group和GCD在此概述:即在另一个dispatch_group的通知方法内embedded一个组是有效的。 另一种更高层次的方式是NSOperations和依赖关系,这也会给予进一步的控制,如取消操作。

大纲

 func doStuffonObjectsProcessAndComplete(arrayOfObjectsToProcess: Array) -> Void){ let firstGroup = dispatch_group_create() for object in arrayOfObjectsToProcess { dispatch_group_enter(firstGroup) doStuffToObject(object, completion:{ (success) in if(success){ // doing stuff success } else { // doing stuff fail } // regardless, we leave the group letting GCD know we finished this bit of work dispatch_group_leave(firstGroup) }) } // called once all code blocks entered into group have left dispatch_group_notify(firstGroup, dispatch_get_main_queue()) { let processGroup = dispatch_group_create() for object in arrayOfObjectsToProcess { dispatch_group_enter(processGroup) processObject(object, completion:{ (success) in if(success){ // processing stuff success } else { // processing stuff fail } // regardless, we leave the group letting GCD know we finished this bit of work dispatch_group_leave(processGroup) }) } dispatch_group_notify(processGroup, dispatch_get_main_queue()) { print("All Done and Processed, so load data now") } } } 

这个答案的其余部分是特定于这个代码库。

这里似乎有一些问题: getAttendees函数接受一个事件子项并返回一个objectIDName都是string? 不应该这个方法返回一个与会者数组? 如果不是,那么返回的objectID是什么?

一旦与会者返回,您可以在一个组中处理它们以获取图片。

getAttendeesPictures最终从Facebook返回UIImages 。 将这些caching到磁盘并传递path ref可能是最好的 – 保留所有这些提取的映像对内存不利,并且根据大小和数量,可能会很快导致问题。

一些例子:

 func getAttendees(child: String, completion: (result: Bool, attendees: Array?) -> Void){ let newArrayOfAttendees = []() // Get event attendees of particular event // process attendees and package into an Array (or Dictionary) // completion completion(true, attendees: newArrayOfAttendees) } func getAttendeesPictures(attendees: Array, completion: (result: Bool, attendees: Array)-> Void){ println("Attendees Count: \(attendees.count)") let picturesGroup = dispatch_group_create() for attendee in attendees{ // for each attendee enter group dispatch_group_enter(picturesGroup) let key = attendee.objectID let url = NSURL(string: "https://graph.facebook.com/\(key)/picture?type=large") let urlRequest = NSURLRequest(URL: url!) //Asynchronous request to display image NSURLConnection.sendAsynchronousRequest(urlRequest, queue: NSOperationQueue.mainQueue()) { (response:NSURLResponse!, data:NSData!, error:NSError!) -> Void in if error != nil{ println("Error: \(error)") } // Display the image let image = UIImage(data: data) if(image != nil){ attendee.image = image } dispatch_group_leave(picturesGroup) } } dispatch_group_notify(picturesGroup, dispatch_get_main_queue()) { completion(true, attendees: attendees) } } func setupEvents(completion: (result: Bool, Event: Event) -> Void){ // get event info and then for each event... getAttendees(child:snapshot.key, completion: { (result, attendeesReturned) in if result { self.getAttendeesPictures(attendees: attendeesReturned, completion: { (result, attendees) in // do something with completed array and attendees } } else { } }) } 

上面的代码只是一个大纲,但希望指出你在正确的方向。

这两个请求同时执行,所以没有与会者从第二个请求执行时获取图片,如果getAttendees完成闭包要被多次调用,那么你可以这样做:

 let group = dispatch_group_create() for key in keys { dispatch_group_enter(group) self.getAttendee(key as String, completion:{ (result, attendee) in if(result == true){ attendees.addEntriesFromDictionary(attendee) self.getAttendeesPictures(attendee, completion: { (result, image) in if result == true { attendeesImages.append(image!) } dispatch_group_leave(group) }) } else { dispatch_group_leave(group) } }) } dispatch_group_notify(group, dispatch_get_main_queue()) {} 

如果第一个请求的结果是完整的参与者集合,那么您甚至不需要使用GCD,只需在完成closures中调用getAttendeesPictures

这段代码并不完全使用原始代码的相同variables和方法,它只是给出了这个想法。

希望能帮助到你!

尽pipe使用GCD和其它方法肯定会有解决scheme,但一般而言,同步是痛苦的,而且越是复杂的代码,它就会显示出越多的问题 – 但我认为这是一个一劳永逸的解决scheme: Bolts框架从Facebook(这两个iOS的iOS)

螺栓框架使用

那么这有什么神奇的呢? 那么,它可以让你创build“任务”,然后链接它们。 特别是你感兴趣的方法是taskForCompletionOfAllTask​​s:,这是为了并行处理,正是你所需要的。 我为你写了一个小例子,你可以根据你的需要进行调整:

 func fetchAllInformation() -> BFTask { // First, create all tasks (if you need more, than just create more, it is as easy as that var task1 = BFTaskCompletionSource() var task2 = BFTaskCompletionSource() var tasks = [task1, task2] // What you do, is you set result / error to tasks and the propagate in the chain upwards (it is either result, or error) // You run task 1 in background API.instance.fetchFirstDetailsInBackgroundWithBlock { (object: AnyObject!, error: NSError!) -> Void in // On error or on success, you assign result to task (whatever you want) if error == nil { task1.setResult(object) } else { task1.setError(error) } } // You run task 2 in background API.instance.fetchSecondDetailsInBackgroundWithBlock { (object: AnyObject!, error: NSError!) -> Void in // On error or on success, you assign result to task (whatever you want) if error == nil { task2.setResult(object) } else { task2.setError(error) } } // Now you return new task, which will continue ONLY if all the tasks ended return BFTask(forCompletionOfAllTasks: tasks) } 

一旦你完成主要的方法,你可以使用螺栓链魔术:

 func processFullObject() { // Once you have main method done, you can use bolts chaining magic self.fetchAllInformation().continueWithBlock { (task : BFTask!) -> AnyObject! in // All the information fetched, do something with result and probably with information along the way self.updateObject() } } 

Bolts框架文档/ README基本上涵盖了所有关于它的知识,而且它是相当广泛的,所以我build议你通过它 – 一旦你得到基础知识,它是非常容易使用。 我个人使用它,这是一个爆炸。 这个答案将有希望为您提供不同的解决scheme和方法,可能是一个更清洁的。

这在概念上是有问题的。 听起来好像你要等到这两个函数完成之后再做其他的事情,但是你没有解释的是getAttendeesPictures 取决于 getAttendeesPictures的结果。 这意味着你真正想要执行一个asynchronous块,然后执行第一个asynchronous块,输出第一个asynchronous块,然后在完成时执行最后的完成块。

GCD不是特别适合这个; 你最好用NSBlockOperations使用NSOperationQueue。 GCD有两个明显的优势:

  1. 与GCD的c型函数相比,NSOperation使用熟悉的面向对象的语法,所以编写和理解起来相当容易。
  2. 队列中的操作可以具有显式的相互依赖关系,因此您可以明确指出,例如操作B只会在操作A完成后执行。

NSHipster有一个很好的写法 ,我build议你去看看。 它主要是在抽象中讨论的,但是你想要做的是使用NSBlockOperation创build两个块操作,一个用于执行getAttendees ,另一个用于执行getAttendeesPictures ,然后明确表示第二个块依赖于第一个块,然后将它们相加排队。 然后他们将执行,并且可以在第二次操作中使用完成块来完成一些事情。

戴夫·罗伯茨(Dave Roberts)的回答是正确的:代码的直接问题在于,你不使用getAttendees函数的输出来实际创build任何与会者。 也许这部分代码是缺less的,但是从我所看到的nameobjectID只是打印出来。 如果你想传递一些有用的东西到getAttendeesPictures函数中,你需要先解决这个问题。

这是我的头顶。 这个想法是只有当所有的嵌套块完成时才读取和处理新的asyc数据。

我们利用while循环来处理等待信号读取下一组数据。

只要完成等于false,while循环就会继续。 除了在等待的时候消耗cpu周期之外,没有什么是真的。 当所有参与者被读取时,循环内部的if仅被触发(设置为true)。

同时在循环内部,我们通过嵌套块进行工作,在参与者中阅读,然后在完成时阅读他们的图片,并在完成时读取firebase数据。 最后,一旦我们获得了先前块的所有数据,我们就会将数据填充到一个对象中,然后将其添加到字典中。 当时确定是否完成了与会者的阅读,如果是的话,完全保释。 如果没有,我们读下一个参加者。

(这是概念上的)

 done = false readyToReadNextAttendee = true while ( done == false ) { if (readyToReadNextAttendee == true ) { readyToReadNextAttendee = false readAttendee readPicture readFirebase { putDataIntoObject addObjectToDictionary if finishedReadingAttendees { done = true } else { readyToReadNextAttendee = true } } } } 

如果您可以select首先阅读所有与会者,则可以遍历和排列,而不读取下一个索引,直到readyToReadNextAttendee = true

我使用的一个想法是在查询语句callback中放置一个if语句检查,并将查询语句callback放在一个for循环中(这样可以遍历所有的查询),所以if语句应该检查这个最后callback预期,那么你应该执行一个return语句或一个deferred.resolve语句,下面是一个概念代码。

 var list=fooKeys //list of keys (requests) i want to fetch form firebase var array=[] // This is the array that will hold the result of all requests for(i=xyz;loop breaking condition; i++){ Ref = new Firebase("https://yourlink.firebaseio.com/foo/" + fooKeys[i]); Ref.once("value", function (data) { array.push(data.val()); if(loop breaking condition == true){ //This mean that we looped over all items return array; //or deferred.resolve(array); } }) } 

把这段代码放在一个函数中并且asynchronous地调用它将使你能够在继续做其他事情之前等待整个结果。

希望你(和其他人)find这个好处。