Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chaining asynchronous operations from an array of objects with ReactiveCocoa

I have an array of entities and I want to perform asynchronous operations on the entities. The operations should be chained and run in the same order with the entities in the array. I'm new to RAC. How to do that in RAC?

like image 967
Arnol Avatar asked Sep 20 '13 01:09

Arnol


1 Answers

First, you'll need a wrapper method that performs your async operation, which will return a signal. Let's assume the async operation operation takes a completion block. From the sounds of it, you don't care about the values, you want the side effects, in which case the signal does not send values, it only completes.

- (RACSignal *)asyncOperation {
    return [RACSignal createSignal:^RACDisposable * (id<RACSubscriber> subscriber) {
        [self asyncOperationWithCompletion:^{
            [subscriber sendCompleted];
        }];
        return nil; // `nil` means there's no way to cancel.
    }];
}

EDIT: Thanks to the comment by Justin Spahr-Summers, here is a much simpler approach to chaining the operations:

RACSignal *signal = [RACSignal concat:[array.rac_sequence map:^(id entity) {
    return [entity asyncOperation];
}]];

ReactiveCocoa's +concat: operator takes a collection of signals and subscribes to the signals one at a time, waiting for completion of one signal before subscribing to its successor. Using -rac_sequence here is for the purpose of mapping the entities to the operation signals.

In this context, the use of +concat: achieves the same effect as the -then: chaining from my initial answer below.


With the RAC wrapper, a solution is to start with an empty signal, and build a chain by iterating over the entities and assembling the operations using -then:. The -then: operation essentially waits for the previous operation to complete before beginning the next.

RACSignal *signal = [RACSignal empty];
for (id entity in array) {
    signal = [signal then:^{
        return [entity asyncOperation];
    }];
}

[signal subscribeCompleted:^{
    // At this point, all operations have completed
}];

At this point what you have is:

[[[[RACSignal empty]
    then:^{ return [entity1 asyncOperation]; }]
    then:^{ return [entity2 asyncOperation]; }]
    // ...
    then:^{ return [entityN asyncOperation]; }]

ReactiveCocoa has helpful documentation and very well documented headers, both of which were very valuable to me when I was new.

like image 117
Dave Lee Avatar answered Nov 05 '22 18:11

Dave Lee