等待异步方法在for循环中完成
我有一个包含三个异步方法的for循环,我希望在这3个异步方法完成后进行一些处理。
-(void)getAllUsersInformations{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for(User *user in users){ [self getUserInfo:user]; } //Here, I want to reload the table view for example, after finishing the for loop (executing the whole three methods). }); } -(void)getUserInfo:(User*)user{ [self getInformations:user]; [self getExperiences:user]; [self getEducation:user]; }
你有任何技术可以得到这个结果吗? 非常感谢你。
一种GCD方法是使用dispatch_group
。 因此,在启动异步任务之前,调用dispatch_group_enter
,然后在异步任务完成时调用dispatch_group_leave
,然后可以创建一个dispatch_group_notify
,它将在异步任务完成时调用。 你可以将它与完成块模式相结合(无论如何,这对异步方法来说是一个好主意):
-
如果
getInformations
,getExperiences
和getEducation
本身就是所有异步方法,那么你需要的第一件事就是了解它们什么时候完成的机制。 一种常见的解决方案是为每个实现完成块模式。 例如:// added completionHandler parameter which will be called when the retrieval // of the "informations" is done. - (void)getInformations:(User*)user completionHandler:(void (^)(void))completionHandler { // do whatever you were before, but in the asynchronous task's completion block, call this // completionHandler() // // for example NSURLRequest *request; [NSURLConnection sendAsynchronousRequest:request queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { // handle the request here // the important thing is that the completion handler should // be called _inside_ the this block if (completionHandler) { completionHandler(); } }]; }
对
getExperiences
和getEducation
也重复这个过程。 -
然后,您可以使用调度组通知您完成这三个请求中的每个请求的时间,并在
getUserInfo
时调用getUserInfo
的完成块:// added completion handler that will be called only when `getInformations`, // `getExperiences` and `getEducation` are all done. // // this takes advantage of the completion block we added to those three // methods above - (void)getUserInfo:(User*)user completionHandler:(void (^)(void))completionHandler { dispatch_group_t group = dispatch_group_create(); // start the three requests dispatch_group_enter(group); [self getInformations:user completionHandler:^{ dispatch_group_leave(group); }]; dispatch_group_enter(group); [self getExperiences:user completionHandler:^{ dispatch_group_leave(group); }]; dispatch_group_enter(group); [self getEducation:user completionHandler:^{ dispatch_group_leave(group); }]; // this block will be called asynchronously only when the above three are done dispatch_group_notify(group, dispatch_get_main_queue(), ^{ if (completionHandler) { completionHandler(); } }); }
-
然后在
getAllUsersInformations
重复此过程:// call new getUserInfo, using dispatch group to keep track of whether // all the requests are done -(void)getAllUsersInformations { dispatch_group_t group = dispatch_group_create(); for(User *user in users){ dispatch_group_enter(group); [self getUserInfo:user completionHandler:^{ dispatch_group_leave(group); }]; } dispatch_group_notify(group, dispatch_get_main_queue(), ^{ [self.tableView reloadData]; }); }
两个最后的想法:
-
概述了所有这些,我必须承认我可能会将这些请求包装在并发/异步自定义
NSOperation
子类中,而不是使用调度组。 请参阅“ 并发编程指南 ”的“配置并发执行操作”部分。 这是对代码进行更彻底的重构,所以我不会在这里解决这个问题,但是它可以限制这些将同时运行的请求的数量,从而减轻潜在的超时问题。 -
我不知道有多少这些用户请求正在进行,但您可能需要考虑在用户信息进入时更新UI,而不是等待所有内容完成。 这又是对代码进行更彻底的重构,但可能会导致更具响应性的内容。
尝试在完成时执行一个块,如果方法是异步的,则不能使用for循环执行此操作。 你必须在完成前一个之后逐个调用getUserInfo。 我想这会解决你的问题。
-(void)getAllUsersInformations{ [self registerUserAtIndex:0]; } - (void) registerUserAtIndex: (NSInteger ) userIndex { RegisterOperation *op = [[RegisterOperation alloc] initWithUser:[users objectAtIndex:userIndex]]; [RegisterOperation setResultCompletionBlock:^(BOOL *finished, NSInteger userIndex) { dispatch_async(dispatch_get_main_queue(), ^{ if (userIndex++ < [users count] { [self registerUserAtIndex:userIndex++]; } else { [myTableView reloadData]; } }]; [[NSOperationQueue mainQueue] addOperation:op]; }
希望这会帮助你。
Rop用swift回答:
func processData() { let group: dispatch_group_t = dispatch_group_create() for item in data as! Object { dispatch_group_enter(group) item.process(completion: {() -> (Void) in dispatch_group_leave(group) }) } dispatch_group_notify(group, dispatch_get_main_queue(), { //Do whatever you want }) }
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Background work for(User *user in users){ [self getUserInfo:user]; } dispatch_async(dispatch_get_main_queue(), ^{ //reload tableview , this is on main thread. }); });