Wait for an async methods to finish in a for loop

前端 未结 4 833
庸人自扰
庸人自扰 2021-02-09 14:38

I have a for loop containing three asynchronous methods, and I want to make some treatment after this 3 async methods are finished.

 -(void)getAllUsersInformatio         


        
4条回答
  •  野的像风
    2021-02-09 15:25

    One GCD approach is to use dispatch_group. So, before you start an asynchronous task, call dispatch_group_enter, and then when the asynchronous task finishes, call dispatch_group_leave, and you can then create a dispatch_group_notify which will be called when the asynchronous tasks finish. You can marry this with a completion block pattern (which is a good idea for asynchronous methods, anyway):

    1. If getInformations, getExperiences and getEducation are, themselves, all asynchronous methods, the first thing you need is some mechanism to know when they're done. A common solution is to implement a completion block pattern for each. For example:

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

      Repeat this process for getExperiences and getEducation, too.

    2. Then, you can use a dispatch group to notify you of when each of these three requests are done done, calling a completion block in getUserInfo when that takes place:

      // 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. And you then repeat this process at the 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];
          });
      }
      

    Two final thoughts:

    1. Having outlined all of that, I must confess that I would probably wrap these requests in concurrent/asynchronous custom NSOperation subclasses instead of using dispatch groups. See the "Configuring Operations for Concurrent Execution" section of the Concurrency Programming Guide. This is a more radical refactoring of the code, so I won't tackle that here, but it lets you constrain the number of these requests that will run concurrently, mitigating potential timeout issues.

    2. I don't know how many of these user requests are going on, but you might want to consider updating the UI as user information comes in, rather than waiting for everything to finish. This is, again, a more radical refactoring of the code, but might lead to something that feels more responsive.

提交回复
热议问题