0

Suppose we have something like this:

while (true) {
  val job = Future { doSomething(); 1 }
  val timeout = Future { Thread.sleep(1000); 2 }

  val both = for (j <- job; t <- timeout) {
    println("Done")
  }
  Await.result(both)
}

What is the idiomatic solution for this using rx-java/scala?

UPDATE: a little more clarification, if not obvious from code.

Let tsn and ten be timestamp of start and end of a doSomething() job respectively.

Then the next job should be scheduled at tsn+1 = max ( ten , tsn + 1 second ).

Tair
  • 3,779
  • 2
  • 20
  • 33

2 Answers2

2

After going through all the possibilities that RxScala and Observables offer I have to say that there may be a fundamental problem here: the subscriber of an observable should not control the emission of new values. The observable is a source of events and the subscriber is a passive consumer. Otherwise for example one subscriber could affect the output the observable emits to other subscribers.

If you still want to use observables this is the best solution I came up with. It zips the ready observable and the timer together so that it emits new event when both the timer and the job are done.

def run(job: () => Unit) {

  val ready = Observable.create{ observer =>
    for(
      j <- future {job(); 1};
    ) observer.onNext(); 
  }

  Observable.timer(1 seconds).zip(ready).subscribe{ value =>
    run();
  }

}

run(doSomenthing); 
Matias Saarinen
  • 606
  • 4
  • 10
  • 1
    What if `doSomething()` takes longer that 1 second? I don't want to have overlapping `doSomething()` calls. – Tair Mar 19 '15 at 14:04
  • First of all since the "Rx promise" the calls never overlap. That's one of the reasons the Rx library is so useful. – Matias Saarinen Mar 19 '15 at 14:50
  • Your question is not actually relevant since you shouldn't create that kind of timer if you can't consume the values. However in some other situation your question would be extremely important. There are basically two ways: just ignore some of the values or postpone them. – Matias Saarinen Mar 19 '15 at 15:29
  • Useful sources for more information: [Introduction to Rx](http://www.introtorx.com/content/v1.0.10621.0/13_TimeShiftedSequences.html#TimeShiftedSequences) and [RxJava documentation](https://github.com/ReactiveX/RxJava/wiki/Backpressure) – Matias Saarinen Mar 19 '15 at 15:30
  • The point is that timer is reset each time the recurring job is finished, I fail to see how this is achieved by `Observable.interval` – Tair Mar 19 '15 at 22:52
  • Ok, I hadn't understood your question correctly. This does not work if you want to reset the timer after each value. – Matias Saarinen Mar 20 '15 at 16:56
0

If I understand the problem correctly, you need to do recursive scheduling (as it seems you don't emit any value from the jobs). Here is an example how to do this with RxJava's Scheduler.Worker:

public class RecurringJob {
    static Subscription runJob(Runnable run) {
        Scheduler.Worker w = Schedulers.newThread().createWorker();
        MultipleAssignmentSubscription mas = new MultipleAssignmentSubscription();
        Action0 action = new Action0() {
            @Override
            public void call() {
                try {
                    run.run();
                    mas.set(w.schedule(this, 1, TimeUnit.SECONDS));
                } catch (Throwable t) {
                    t.printStackTrace();
                    w.unsubscribe();
                }
            }
        };
        mas.set(w.schedule(action, 1, TimeUnit.SECONDS));
        return mas;
    }
    public static void main(String[] args) throws InterruptedException {
        Subscription s = runJob(() -> System.out.println("Job"));
        Thread.sleep(10000);
        s.unsubscribe();
        System.out.println("Job stopped");
        Thread.sleep(3000);
        System.out.println("Done.");
    }
}
akarnokd
  • 69,132
  • 14
  • 157
  • 192