This question already has answers here:
RxJava delay for each item of list emitted
(17 answers)
Closed 6 years ago.
I have an observable I've created from a list of objects. For each object in the list I make a network request but I'd like to put a delay between each item in the list as to space out the requests a bit. Here's a snippet of my code.
return Observable.from(documentGroupModels).flatMap(new Func1<DocumentGroupModel, Observable<Boolean>>() {
#Override
public Observable<Boolean> call(DocumentGroupModel documentGroupModel) {
return refreshDocumentWithUri(documentGroupModel.getUri(), documentGroupModel.sectionGroupId,
includeExceptions, false);
}
});
Using delay or buffer doesn't quite work for this scenario as far as I can tell.
You can use a combination of Zip and interval operator if your delay is static, so you can emit an item of your zip every time configure on your interval.
Check the example
#Test
public void delaySteps() {
long start = System.currentTimeMillis();
Subscription subscription =
Observable.zip(Observable.from(Arrays.asList(1, 2, 3)), Observable.interval(200, TimeUnit.MILLISECONDS),
(i, t) -> i)
.subscribe(n -> System.out.println("time:" + (System.currentTimeMillis() - start)));
new TestSubscriber((Observer) subscription).awaitTerminalEvent(3000, TimeUnit.MILLISECONDS);
}
Also you can create an Observable with your list and use concatMap, then you can use delay for every item emitted. Maybe this solution is more elegant and no so Hacky
#Test
public void delayObservableList() {
Observable.from(Arrays.asList(1, 2, 3, 4, 5))
.concatMap(s -> Observable.just(s).delay(100, TimeUnit.MILLISECONDS))
.subscribe(n -> System.out.println(n + " emitted"),
e -> {
},
() -> System.out.println("All emitted"));
new TestSubscriber().awaitTerminalEvent(1000, TimeUnit.MILLISECONDS);
}
You can see another examples of delay here https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/utils/ObservableDelay.java
Use the `delay' operator e.g.
return Observable.from(documentGroupModels).flatMap(new Func1<DocumentGroupModel, Observable<Boolean>>() {
#Override
public Observable<Boolean> call(DocumentGroupModel documentGroupModel) {
return refreshDocumentWithUri(documentGroupModel.getUri(), documentGroupModel.sectionGroupId,
includeExceptions, false).delay(2000, TimeUnit.MILLISECONDS);
}
});
Related
If i execute such a code:
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Flowable.fromIterable(Lists.newArrayList(1, 2, 3, 4, 5, 6))
.subscribeOn(Schedulers.io())
.subscribe(new Consumer<Integer>() {
#Override
public void accept(Integer integer) throws Throwable {
System.out.println(stopWatch + " value:" + integer);
Thread.sleep(1000);
}
});
Thread.sleep(100000);
In output, i get each element just after sleep time, like this:
00:00:00.027 value:1
00:00:01.030 value:2
00:00:02.030 value:3
00:00:03.031 value:4
00:00:04.031 value:5
00:00:05.031 value:6
but as i understand, if i use Schedulers.io(), than i must get all the values parallel, and i expected, that i will get all values immediately, than i will wait 1000 mills just once, and thats all
Like this:
00:00:00.027 value:1
00:00:00.030 value:2
00:00:00.030 value:3
00:00:00.031 value:4
00:00:00.031 value:5
00:00:00.031 value:6
How can i get them all in other threads, NOT one by one?
I dont wanna them to wait each other
I try Schedulers.computation() and other, but they still arrive one by one
How to get them all immediately?
P.S.
There is some text for better search in google. I get it from browser history.
rxjava how to many subscriber, rxjava how to post all elements synchronously, only one thread active rxjava, flowable from don't work in many threads
if i use Schedulers.io(), than i must get all the values parallel
No. RxJava flows are sequential by default, which means items are delivered one after the other. If your consumer blocks or sleeps, subsequent items are delayed every time.
How can i get them all in other threads, NOT one by one?
Use parallel:
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Flowable.fromIterable(Lists.newArrayList(1, 2, 3, 4, 5, 6))
.parallel()
.runOn(Schedulers.io())
.doOnNext(new Consumer<Integer>() {
#Override
public void accept(Integer integer) throws Throwable {
System.out.println(stopWatch + " value:" + integer);
Thread.sleep(1000);
}
})
.sequential()
.subscribe();
Thread.sleep(100000);
Recommended reading: https://github.com/ReactiveX/RxJava#parallel-processing
I saw this question here.
It's about achieving delay for each emitted item. This is how to achieve it based on the accepted answer:
Observable.zip(Observable.range(1, 5)
.groupBy(n -> n % 5)
.flatMap(g -> g.toList()),
Observable.interval(50, TimeUnit.MILLISECONDS),
(obs, timer) -> obs)
.doOnNext(item -> {
System.out.println(System.currentTimeMillis() - timeNow);
System.out.println(item);
System.out.println(" ");
}).toList().toBlocking().first();
In the question, the asker specifically asked for a fixed set of observables (Observable.range(1,5)), unfortunately this is not what I want to achieve.
I also saw this comment.
That comment is what I want to achieve. So my source observable emits items at a slower (and sometimes faster) rate than the interval. Also the observable's emits are never ending.
===
So basically I want hot observables to have minimum delay.
For example, if I want 400ms minimum delay and I have this kind of observable emittance:
X1-100ms delay-X2-200ms delay-X3-600ms delay-X4-20000ms delay-X5-...
I want it to yield:
X1-400ms delay-X2-400ms delay-X3-600ms delay-X4-20000ms delay-X5-...
Anybody have any idea to achieve that?
Your requirement is so strange...
I can resolve it but not elegant. Here is my code:
class Three<A, B, C> {
A a;
B b;
C c;
// Getter, Setter, Constructor
}
public static void main(String[] args) throws Exception {
BehaviorSubject<Integer> s = BehaviorSubject.create();
// Three = (The value, upstream comes mills, downstream emits mills)
s.map(i -> new Three<>(i, System.currentTimeMillis(), System.currentTimeMillis()))
.scan((a, b) -> {
b.setC(a.getC() + Math.max(400L, b.getB() - a.getB()));
return b;
})
.concatMap(i -> Observable.just(i.getA()).delay(Math.max(0, i.getC() - System.currentTimeMillis()),
TimeUnit.MILLISECONDS))
.subscribe(i -> System.out.println(i + "\t" + System.currentTimeMillis()));
s.onNext(0);
Thread.sleep(100);
s.onNext(1);
Thread.sleep(200);
s.onNext(2);
Thread.sleep(600);
s.onNext(3);
Thread.sleep(2000);
s.onNext(4);
Thread.sleep(200);
s.onNext(5);
Thread.sleep(800);
s.onNext(6);
Thread.sleep(1000);
}
and output
0 1510128693984
1 1510128694366 // 400ms
2 1510128694766 // 400ms
3 1510128695366 // 600ms
4 1510128697366 // 2000ms
5 1510128697766 // 400ms
6 1510128698567 // 800ms
I've been stuck with this for a day. Inspired in Dan Lew great post, I tried to make a simple testcase for repeatWhen() and retryWhen():
public class ObsTest {
private static final Logger LOG = LoggerFactory.getLogger(ObsTest.class);
#Test
public void test1() throws InterruptedException {
Observable<Integer> obs = rx.Observable.<Integer> create(observer -> {
LOG.info("onSubscribe");
Integer data = RandomUtils.nextInt(0, 1000);
if (data % 2 != 0) {
observer.onError(new RuntimeException("Odd number " + data));
} else {
observer.onNext(data);
}
observer.onCompleted();
}, BackpressureMode.BUFFER);
obs.repeatWhen(completed -> completed.delay(1, TimeUnit.MILLISECONDS))
.retryWhen(error -> error.delay(1, TimeUnit.MILLISECONDS))
.subscribe(i -> LOG.info("value={}", i), e -> LOG.info("Exception = {}", e.getMessage()));
}
My idea is this should run forever, emitting even numbers as "correct" results, and odd numbers as "errors".
Instead, this runs for one or two loops and then stops. And that is when the delay is 1 millisecond, for longer periods of time (ie. 1 second), it runs a single time, emitting just a single odd or even number.
I'm sure I'm doing something wrong, but I can't find what it is.
When you call delay which uses Schedulers.computation() by default you are introducing asynchrony. Once activity starts occurring in a background thread your test will finish and presumably your process is exited. You need to use a blockingSubscribe or put a longish Thread.sleep at the end.
As Dave Moten mentioned, delay uses Schedulers.computation() by default, but you can pass scheduler of your choice instead - for tests purposes you may use TestScheduler and "take control over time". Code below shows how can it be used - as you can see this subscription won't terminal for another 30 days, what is basically forever ;)
public class ObsTest {
#Test
public void test1() {
Observable<Integer> obs = rx.Observable.create(observer -> {
Integer data = RandomUtils.nextInt(0, 1000);
if (data % 2 != 0) {
observer.onError(new RuntimeException("Odd number " + data));
} else {
observer.onNext(data);
}
observer.onCompleted();
}, Emitter.BackpressureMode.BUFFER);
TestScheduler scheduler = Schedulers.<Integer>test();
AssertableSubscriber subscriber = obs.repeatWhen(completed -> completed.delay(1, TimeUnit.MILLISECONDS, scheduler))
.retryWhen(error -> error.delay(1, TimeUnit.MILLISECONDS, scheduler))
.subscribeOn(scheduler)
.test();
subscriber.assertNoValues();
scheduler.advanceTimeBy(30, TimeUnit.SECONDS);
subscriber.assertNoTerminalEvent();
scheduler.advanceTimeBy(30, TimeUnit.DAYS);
subscriber.assertNoTerminalEvent();
}
}
I have the following code:
public void onEnter() {
Observable<GObject> obs = context.update(activeG);
obs.subscribe((gObj) -> {
//TODO: add delay of 3 sec.
activeG.publishNewG();
activeG.nextState(GType.RUNNING);
});
}
My question is, How can I put a delay of 3 seconds so
activeG.publishNewG()
is called, then delay of 3 seconds, and then a call to
activeGame.nextState(GameStateType.RUNNING);
'publishNewG' returns void.
Thank you!
If I understand correctly, you want to put a 3 second delay between publishNewG and nextState. You can use doOnNext to inject activity at certain points in the sequence, for example, before and after a 3 second delay:
Observable.just(1)
.doOnNext(e -> System.out.println("Action before"))
.delay(3, TimeUnit.SECONDS)
.doOnNext(e -> System.out.println("Action after"))
.toBlocking().first();
You would typically accomplish this using a scheduler of some sort. Java has ScheduledExecutorService that does this for you.
public class MyClass {
private final ScheduledExecutorService scheduler;
public MyClass() {
scheduler = Executors.newSingleThreadScheduledExecutor();
}
public void onEnter() {
Observable<GObject> obs = context.update(activeG);
obs.subscribe((gObj) -> {
activeG.publishNewG();
// Run in 3 seconds
scheduler.schedule(() -> {
activeG.nextState(GType.RUNNING);
}, 3, TimeUnit.SECONDS);
});
}
}
Edit: From what I can understand of the RxJava docs this is how you would do it with delay:
public void onEnter() {
Observable<GObject> obs = context.update(activeG);
// Observable that delays all events by 3 seconds
Observable<GObject> delayed = obs.delay(3, TimeUnit.SECONDS);
// This will fire immediately when an event happens
obs.subscribe((gObj) -> {
activeG.publishNewG();
});
// This will fire 3 seconds after an event happens
delayed.subscribe((gObj) -> {
activeG.nextState(GType.RUNNING);
});
}
You can easily add delay by using the timer operator.
For example:
// simulated stream of events spread apart by 400ms
Observable<Long> yourObservable = Observable.interval(400, TimeUnit.MILLISECONDS).take(3);
yourObservable.flatMap(data -> {
// add delay of 3 seconds
return Observable.timer(3, TimeUnit.SECONDS).map(i -> data);
}).map(dataAfterDelay -> {
// do whatever you want after 3 seconds
System.out.println("got data " + dataAfterDelay + " after 3 seconds");
return dataAfterDelay + " processed after delay";
}).toBlocking().forEach(System.out::println); // toBlocking here just as example to block main thread
AFAIK you can use Thread.sleep(3000)
If not, something like this should do:
long start = new Date().getTime();
while(new Date().getTime() - start < 3000L){}
I'm trying to implement a class that emits changes using an Observable.
When a subscription is done to this observable I want to send an starting/initialization event. Then I want to send the usual events.
For example. Lets say I have two different subscribers A and B. A and B starts subscribing at different times. If MyClass.getChanges() emits event no. 1,2,3,4 and 5.
If A starts it subscription between event 1,2 then it should receive the following events:
InitialEvent, 2, 3, 4, 5.
If B starts it subscription between event 4 and 5, then B should receive the following events:
InitialEvent, 5.
How to do this using RxJava?
Thanks!
Edit 1
I think I need to explain that the "InitialEvent" is different each time it's emitted. It's calculated by MyClass each time a new subscriber starts to subscribe from getChanged().
My scenario is that MyClass contains a list. The "initialEvent" contains the list at the moment when the subscription is done. Then each change to this list is emitted from getChanges().
Sorry to post this 2 years later, but I had the same need and found this question unanswered.
What I did is the following:
public Observable<Event> observe() {
return Observable.defer(() ->
subject.startWith(createInitialEvent())
);
}
The idea is the following:
defer() executes the passed-in lambda expression when an observer subscribes to the Observable returned by the method observe(). So basically, it executes subject.startWith(...), which returns an Observable that is the actual source of event for the subscriber.
subject.startWith(...) emits an initial event (specified by startWith(...)) followed by those emitted by the subject.
So, if I come back to the original post:
if an observer starts it subscription between event 1,2 then it should receive the following events: InitialEvent, 2, 3, 4, 5.
What you're looking for is PublishSubject. Subjects are hot Observables, in that they do not wait for Observers to subscribe to them before beginning to emit their items. Here's a bit of info on Subjects.
Here's a quick demo of your use-case
PublishSubject<String> subject = PublishSubject.create();
Observable<String> InitEvent = Observable.just("init");
Observable<String> A = subject.asObservable();
Observable<String> B = subject.asObservable();
subject.onNext("1");
A.startWith(InitEvent)
.subscribe(s -> System.out.println("A: " + s));
subject.onNext("2");
subject.onNext("3");
subject.onNext("4");
B.startWith(InitEvent)
.subscribe(s -> System.out.println("B: " + s));
subject.onNext("5");
Possibly not really elegant way how about just using a flag? It looks like you just want to replace the first emitted event.
e.g. for one subscription the following logic:
boolean firstTimeA = true;
myCustomObservable.subscribe(s -> {
System.out.println(firstTimeA ? "initEvent" : s.toString());
if(firstTimeA) firstTimeA = false;
});
And since you want to have a second subscription just create a firstTimeB and update it your B subscription.
If I understand what you are asking something like this should work for you
int last = 0;
Observable obs;
List<Integer> list = new ArrayList<>();
public SimpleListObservable() {
obs = Observable.create(new Observable.OnSubscribe<Integer>() {
#Override
public void call(Subscriber<? super Integer> subscriber) {
while(last < 30) {
last++;
list.add(last);
subscriber.onNext(last);
}
subscriber.onCompleted();
}
});
}
public Observable<Integer> process() {
return Observable.from(list).concatWith(obs);
}
As the source observable collects values they are added to the List (you can transform the items as you see fit, filter them out, etc) and then when ObserverB subscribes it will get a replay of the items already collected in the List before continuing with the source observable output.
This simple test should demonstrate the outcome
public void testSequenceNext() {
final SimpleListObservable obs = new SimpleListObservable();
final Observer<Integer> ob2 = Mockito.mock(Observer.class);
obs.process().subscribe(new Observer<Integer>() {
#Override
public void onCompleted() {
ob1Complete = true;
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(Integer integer) {
System.out.println("ob1: " + integer);
if (integer == 20) {
obs.process().subscribe(ob2);
}
}
});
ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
Mockito.verify(ob2, Mockito.times(30)).onNext(captor.capture());
for (Integer value : captor.getAllValues()) {
System.out.println(value);
}
}
What do you think of this, I've made part of my API of course as I'm on a phone :
public class StreamOfSomething {
new StreamOfSomething() {
// source of events like
events = Observable.range(0, 1_000_000_000)
.doOnNext(set::add) // some operation there
.map(Event::change)
.publish()
.refCount();
}
public Observable<Event> observeChanges() {
return events.startWith(
Observable.just(Event.snapshot(set))); // start stream with generated event
}
}
And the client can do something like :
Observable.timer(2, 4, TimeUnit.SECONDS)
.limit(2)
.flatMap(t -> theSourceToWatch.observeChanges().limit(10))
.subscribe(System.out::println);
Note however if you are in a multithreaded environment you may have to synchronize when you are subscribing to block any modification, otherwise the list may change before it get's emitted. Or rework this class completely around observables, I don't know yet how to achieve this though.