0

How would you perform N asynchronous operations, such as network calls, working with completion block operations and no delegates/notifications?

Given N methods like this:

- (void)methodNWithCompletion:(void (^)(Result *))completion {
    Operation *operation = [Operation new];

    // ...
    // Asynchronous operation performed here
    // ...

    return;
}

A straightforward solution would be to call each operation in the completion block of the previous one:

[self method1WithCompletion:^(Result *result) {
    // ...
    [self method2WithCompletion:^(Result *result) {
        // ...
        [self method3WithCompletion:^(Result *result) {
            // ...
            [self method4WithCompletion:^(Result *result) {
                NSLog(@"All done");
            }
        }
    }
}

but I'm looking for a more elegant and reusable solution, easier to write and maintain (with no many indented blocks).

Many thanks, DAN

DAN
  • 919
  • 1
  • 6
  • 23
  • 1
    If the question is "how do I know when they're done", you'd generally use `dispatch_group`. So, enter the group before doing each asynchronous call, exit the group in the completion handler of the asynchronous call, and then configure a `dispatch_group_notify` to be performed when all of the asynchronous calls are done. E.g., http://stackoverflow.com/a/29716069/1271826 or http://stackoverflow.com/a/34532865/1271826. – Rob Oct 30 '16 at 21:13

1 Answers1

3

It all depends on what you want to do. Many powerful sophisticated tools are at your disposal. You can use such things as:

  • Serial queue (if you want the completion blocks run in order)

  • Concurrent queue (if you don't care whether the completion blocks execute simultaneously or in what order)

  • Dispatch group (if there is something you want to do only after all completion blocks have finished)

  • Operation and OperationQueue (if you want to establish the dependency order in which networking operations must take place - see esp. the fantastic WWDC 2015 video on this topic)

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • I need the completion blocks to be executed in a given order and some code to be executed after all of them have ended. I was trying to write a solution with no Operation/OperationQueue in order to keep the code quite simple. – DAN Oct 30 '16 at 21:19
  • Then, reading what I said in my answer, you would use a serial queue and a dispatch group. In my opinion Operation is more expressive and can handle the unexpected more gracefully, but you can manage without it if you are using completion blocks and not delegate methods. – matt Oct 30 '16 at 21:19
  • Yes, I'll definitely have a look at that approach, thank you! – DAN Oct 30 '16 at 21:20
  • @DAN - "I need the completion blocks to be executed in a given order..." - Are you sure? Do you absolutely need to use the results of one in order to start the subsequent one? You pay a huge performance penalty for requiring them to be executed in a particular order. If you have to, so be it, but then that suggests broader design problems. – Rob Oct 30 '16 at 21:23
  • @matt - Let's assume, for a second, that he does need to run these sequentially. You might want to show him how to use serial queue to run a bunch of tasks that are, themselves, asynchronous. It's not as simple as "use serial queue" might suggest. It seems like a disservice to send Dan down that road... – Rob Oct 30 '16 at 21:27
  • @Rob unfortunately I do. I was in fact trying to understand how complex working with serial queues is and if it is worth it. Otherwise I was having a look at Promise-based solutions too. – DAN Oct 30 '16 at 21:31
  • @Rob feel free to add an answer. I answered the vague question actually asked. I have no idea what the OP's actual requirements are and I'm not convinced that he does either, but his question certainly didn't say what they were. – matt Oct 30 '16 at 21:32
  • 1
    This has been asked and answered many times, so I'm not inclined to post another answer on this topic. My point is that Dan asked "I have a bunch of asychronous methods with completion handlers" and I just wanted to make sure that he knew that he can't just add those to a serial queue and expect that to work. I'd suggest operations or promises/futures. Or just do something simple like only initiating one request in the completion handler of the prior one. – Rob Oct 30 '16 at 21:51
  • Thank you guys, all your comments have been really useful. I'll try both the dispatch_group and promises solutions. – DAN Oct 30 '16 at 22:12