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?
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.

- 6,299
- 1
- 36
- 36
-
3You can use `+concat:` to achieve something similar without applying `-then:` repeatedly. – Justin Spahr-Summers Sep 23 '13 at 06:16
-
I think I chose `+then:` to communicate that there are no values, only sequencing of operations. – Dave Lee Sep 23 '13 at 15:44
-
`-then:` is just equivalent to `+defer:` & `-concat:` & `-ignoreValues`. You could add the latter to explicitly indicate that you don't care about the values sent. Also, note that `+concat:` is not `-concat` is not `-concat:` (though they all do very similar things). – Justin Spahr-Summers Sep 23 '13 at 17:59
-
I also thought `-then:` would read easiest. While not my intention, the implicit `+defer:` may be a good thing for a newcomer, in case they unintentionally do side effects in `-asyncOperation`. – Dave Lee Sep 23 '13 at 19:22
-
Thanks for correcting me about `+concat:`! I forgot about it, and thought you must have meant `-concat` (which was ignorant on my part). I've updated my answer. – Dave Lee Sep 23 '13 at 19:25
-
So how to run async operations concurrently, I want to control the maximum number of concurrent operations. For example, 2 operations will run concurrently, if one of them completed, a new one will be started. – Arnol Sep 25 '13 at 03:35
-
When I put this iteration over the array inside an then block, the asyncOperation gets called twice. Is this a bug? – Johan Karlsson Feb 25 '14 at 19:46
-
Hi @JohanKarlsson, can you create a new question and show the code? – Dave Lee Feb 26 '14 at 05:16
-
Actually I found the answer here: http://stackoverflow.com/questions/20275570/why-the-signal-is-called-twice-in-reactivecocoa That is that I subscribed to the complete call twice. Removed it from the inner parts and then it worked. Good to keep the link here for other people that just copy and paste this code :) – Johan Karlsson Feb 26 '14 at 06:26