2014-10-13 2 views
6

У меня есть цикл for, содержащий три асинхронных метода, и я хочу сделать некоторую обработку после того, как эти 3 асинхронных метода закончены.Подождите, пока методы async закончатся в цикле for

-(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]; 
} 

У вас есть техническая информация, которая будет иметь этот результат? Большое спасибо.

+0

ли 'getInformations',' 'getExperiences' и getEducation', сами асинхронные методы? Или это все синхронные методы, которые вы отправляете асинхронно в какую-то фоновую очередь? (Ваше изменение в вопросе радикально меняет ответ.) – Rob

+0

@Rob; Да, они асинхронны! 'getAllUsersInformations'' getInformations' 'getExperiences' и' getEducation' являются асинхронными. – androniennn

+0

@androniennn вы можете сделать три метода '-getXXX' синхронными? –

ответ

14

Один подход GCD заключается в использовании dispatch_group. Итак, прежде чем вы начнете асинхронную задачу, вызовите dispatch_group_enter, а затем, когда завершится асинхронная задача, вызовите dispatch_group_leave, и затем вы можете создать dispatch_group_notify, который будет вызываться, когда завершатся асинхронные задачи. Вы можете жениться на это с рисунком завершения блока (который является хорошей идеей для асинхронных методов, во всяком случае):

  1. Если 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 тоже.

  2. Затем вы можете использовать диспетчерскую группу, чтобы уведомить вас о том, когда каждый из этих трех запросов сделаны сделано, вызывая блок завершения в 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(); 
         } 
        }); 
    } 
    
  3. И затем повторите этот процесс в 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]; 
        }); 
    } 
    

Две заключительные мысли:

  1. Изложив все это, я должен признаться, что я, вероятно, обернуть эти запросы в параллельном/асинхронном пользовательских NSOperation подклассы вместо использования диспетчерских групп. См. Раздел «Настройка операций для параллельного выполнения» в разделе Concurrency Programming Guide. Это более радикальный рефакторинг кода, поэтому я не буду заниматься этим, но он позволяет вам ограничить количество этих запросов, которые будут выполняться одновременно, смягчая потенциальные проблемы с таймаутом.

  2. Я не знаю, сколько из этих пользовательских запросов происходит, но вы можете подумать о том, чтобы обновить пользовательский интерфейс по мере поступления информации о пользователе, а не ждать завершения всего. Это, опять же, более радикальный рефакторинг кода, но может привести к чему-то более чувствительному.

+0

Какое объяснение! Понял технику, и я постараюсь ее реализовать. Я оставлю отзыв;). – androniennn

+0

последний 'group_notify', который перезагружает табличное представление, никогда не вызывается. Я не знаю, почему именно потому, что вызывается 'group_leave'' getUserInfo'. – androniennn

+0

Во-первых, убедитесь, что последнее уведомление группы не было вызвано (и что это не просто проблема, что перезагрузка таблицы не работает должным образом). Во-вторых, чтобы диагностировать это, зарегистрируйте группу enter и оставьте в 'getAllUsersInformations' и убедитесь, что они вызвали _all_. – Rob

-1
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. 
    }); 
}); 
+0

Три метода в '-getUserInfo:' являются асинхронными, не заставит ли это вернуться раньше? –

+0

@AaronA. : Точно Аарон. – androniennn

+0

Нет, эти методы будут называться синхронно на фоне. Действительно, если вы поместите контрольную точку при входе в блок, она пройдет через нее. Когда цикл for будет завершен, блок 'dispatch_async (dispatch_get_main_queue(),^{ // перезагружает tableview, это основной поток. });' будет вызываться в основном потоке. Сделайте быстрый тест, он должен работать. –

0

Попробуйте сделать блок с завершением, вы не можете сделать это с помощью цикла 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]; 
} 

Надеюсь, это вам подойдет.

0

Rop Ответ с быстрой:

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 
     }) 
} 
Смежные вопросы