How to handle 3 differents yet simultaneous async calls

117 Views Asked by At

Here is my concern: For some reason, I have to retrieve and group the answer coming from 3 different web request results. Once the 3 of them have return, I must then call a completionBlock and pass it all the results. Using NSOperation API, I managed to do something like that :

Session * session = [[ServiceLocator sharedInstance] serviceForName:ServiceLocatorNameUserRepository];


NSString * urlMethod = @"POST";
NSDictionary * params = @{kParkadomWebServiceUser : session.currentUser.userID};

NSMutableDictionary * records = [NSMutableDictionary new];

NSOperation * currentBooking = [self performRequestWithPath:API_BOOKING_INPROGRESS
                                                         method:urlMethod
                                                      parameter:params
                                                     completion:^(id json, NSError * error)
                                                     {
                                                         records[@"current"] = error ? error : json;

                                                     }];

NSOperation * upcomingBookings = [self performRequestWithPath:API_BOOKING_UPCOMING
                                                         method:urlMethod
                                                      parameter:params
                                                     completion:^(id json, NSError * error)
                                                     {
                                                         records[@"upcoming"] = error ? error : json;

                                                     }];

NSOperation * pastBookings = [self performRequestWithPath:API_BOOKING_HISTORY
                                                   method:urlMethod
                                                parameter:params
                                               completion:^(id json, NSError * error)
                                               {
                                                   records[@"past"] = error ? error : json;

                                               }];


NSBlockOperation * completionOperation = [NSBlockOperation blockOperationWithBlock:^{
    completion([records copy], nil);
}];
[completionOperation addDependency:currentBooking];
[completionOperation addDependency:upcomingBookings];
[completionOperation addDependency:pastBookings];

[[NSOperationQueue mainQueue] addOperation:completionOperation];

where completion is obviously the completion block given as an entry parameter. The performRequestWithPath:method:parameter:completion: create an NSOperation, adds it to a global queue and returns it, so I have my 3 web calls here. Then I create a block operation and adds the dependency to the 3 previous block so it is not fired before the 3 blocks did complete.

My concern is over the NSMutableArray thingy. It's seems like a poor design to do something like this and I'm not sure if it's really bug proof in case 2 calls finished exactly at the same time. (note that I made sure in the perform...: method that the completion block is called in the main queue).

Any feedback? Suggestions, criticism, theory... I'm all open :)

2

There are 2 best solutions below

0
On BEST ANSWER

You will need to add synchronization.

@synchronized(myArray) {
  [myArray doSomething];
}

For example, in your code:

@synchronized(records) {
    records[@"past"] = error ? error : json;
}

Edit: @gnasher729 brings up the good point that if they completion blocks are run on the main thread, then there is no need for synchronization.

5
On

If your "records" dictionary is used for the single purpose of key/values being added in these asynchronous calls, you can do this easily by putting @synchronized (records) { ... } around the code that adds the three values.

If the completion blocks execute on the main queue, then they cannot execute at the same time. However, you'd have to check your code carefully: The three operations might finish before the completion blocks finish!