简体   繁体   中英

ReactiveCocoa - Stop the triggering of subscribeNext until another signal has been completed

I'm pretty new to FRP and I'm facing a problem:

I subscribe to an observable that triggers subscribeNext every second.

In the subscribeNext 's block, I zip observables that execute asynchronous operations and in zip 's completed block I perform an action with the result.

let signal: RACSignal
let asynchOperations: [RACSignal]
var val: AnyObject?

// subscribeNext is trigered every second
signal.subscribeNext {
  let asynchOperations = // several RACSignal

  // Perform asynchronous operations
  RACSignal.zip(asynchOperations).subscribeNext({
    val = $0
  }, completed: {
    // perform actions with `val`
  })
}

I would like to stop the triggering of subscribeNext for signal (that is normally triggered every second) until completed (from the zip ) has been reached.

Any suggestion?

It sounds like you want an RACCommand .

A command is an object that can perform asynchronous operations, but only have one instance of its operation running at a time. As soon as you tell a command to start execute: ing, it will become "disabled," and will automatically become enabled again when the operation completes.

(You can also make a command that's enabled based on other criteria than just "am I executing right now," but it doesn't sound like you need that here.)

Once you have that, you could derive a signal that "gates" the interval signal (for example, if:then:else: on the command's enabled signal toggling between RACSignal.empty and your actual signal -- I do this enough that I have a helper for it), or you can just check the canExecute property before invoking execute: in your subscription block.


Note: you're doing a slightly weird thing with your inner subscription there -- capturing the value and then dealing with the value on the completed block.

If you're doing that because it's more explicit, and you know that the signal will only send one value but you feel the need to encode that directly, then that's fine. I don't think it's standard, though -- if you have a signal that will only send one value, that's something that unfortunately can't be represented at the type level, but is nonetheless an assumption that you can make in your code (or at least, I find myself comfortable with that assumption. To each their own).

But if you're doing it for timing reasons, or because you actually only want the last value sent from the signal, you can use takeLast:1 instead to get a signal that will always send exactly one value right at the moment that the inner signal completes, and then only subscribe in the next block.


Slight word of warning: RACCommand s are meant to be used from the main thread to back UI updates; if you want to use a command on a background thread you'll need to be explicit about the scheduler to deliver your signals on (check the docs for more details).


Another completely different approach to getting similar behavior is temporal recursion: perform your operation, then when it's complete, schedule the operation to occur again one second later, instead of having an ongoing timer.

This is slightly different as you'll always wait one second between operations, whereas in the current one you could be waiting anywhere between zero and one seconds, but if that's a not a problem then this is a much simpler solution than using an RACCommand .

ReactiveCocoa's delay: method makes this sort of ad-hoc scheduling very convenient -- no manual NSTimer wrangling here.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM