Perhaps I am misunderstanding some basic concept, but it looks like a bug to me. When converting an observable of arrays to observable of individual items, the resulting observable never completes. More specifically, .debug() // #1 line does log completion event, but .debug() // #2 does not. Any ideas on how to make the 'items' observable complete upon completion of the 'array' observable?
let array: Observable<[Any]> = Observable.just([0])
let items = array.flatMap {
Observable.from($0)
.debug() // #1
}
.debug() // #2
Observable
.just([0, 1])
.debug("0>")
.flatMap({ Observable.from($0) })
.debug("2>")
.subscribe()
.disposed(by: disposeBag)
The log is:
0> -> subscribed
0> -> Event next([0, 1])
0> -> Event completed
0> -> isDisposed
2> -> subscribed
2> -> Event next(0)
2> -> Event next(1)
2> -> Event completed
2> -> isDisposed
So, it works for me. Are you sure you've done it the way you posted when received unexpected results?
Related
I am experimenting Dart, and I can't explain two observations.
I wonder why the "onDone" handler assigned to a stream subscription does not fire up.
I wonder why the "onPause" and "onResume" handlers fire up only once.
The code:
import 'dart:async';
import 'dart:io';
/// This class encapsulates all the necessary data used by the "onValue" event
/// handler (the construct avoids using global variables).
class OnValueHandlerContainer {
static StreamSubscription<int> _streamSubscriber;
static setStreamSubscriber(StreamSubscription<int> stream) {
_streamSubscriber = stream;
}
// This static method is the handler executed when a event is received through
// the stream.
//
// WARNING: you have absolutely no idea when this handler will be executed.
// Do not assume that it will be executed right after the execution of the code
// that emits an event. It may be executed several lines (of codes) below the
// line that emits the event. It may well be executed after the end of the
// script.
static void onValue(int value) {
// At this point: the state of the subscription is (inevitably) "active".
print("onValue: An event has been raised. The associated value is ${value}!");
print(" Pause the subscription. Wait for 1 second. Resume the subscription");
// Note 1: once a Dart function starts executing, it continues executing until
// it exits. When managing interrupts in C, it is necessary to protect
// interrupt handlers from being interrupted. This is not the case in
// Dart : a function (and, thus, an event handler) cannot be interrupted
// by the occurrence of another event.
// => The code below has no sense, other than experimentation.
// Note 2: while paused, the subscription will not fire any events. If it receives
// events from its source, they will be buffered until the subscription
// is resumed.
_streamSubscriber.pause();
sleep(Duration(seconds: 1));
_streamSubscriber.resume();
// At this point: the state of the subscription is "active".
}
}
main() async {
// Create a controller.
// A StreamController gives you a new stream and a way to add events to the stream
// at any point, and from anywhere. The stream has all the logic necessary to handle
// listeners and pausing. You return the stream and keep the controller to yourself.
StreamController<int> sc = StreamController<int>(
onListen: () => print("Controller: the stream has been assigned a listener!"),
onCancel: () => print("Controller: the stream has been canceled!"),
// As you may notice, the event handlers are not executed every time the
// subscription gets paused or resumed.
//
// This behaviour comes from these facts:
// - Dart is single-threaded.
// - An event handler cannot be interrupted: once a Dart function starts
// executing, it continues executing until it exits. In other words, Dart
// functions can’t be interrupted by other Dart code.
// See https://webdev.dartlang.org/articles/performance/event-loop
// - A stream is a FIFO.
onPause: () => print("Controller: the stream has been paused!"),
onResume: () => print("Controller: the stream has been resumed!")
);
// Get the stream created by the stream controller.
// Right now, this stream has no assigned listener.
Stream<int> stream = sc.stream;
print("Does the stream provided by the controller have a listener ? ${sc.hasListener ? 'yes' : 'no'} - the answer should be no.");
// Push values into the stream controlled by the stream controller.
// Because no listener subscribed to the stream, these values are just stored
// into the stream.
for(int i=0; i<3; i++) {
print("Send the value ${i} into the stream.");
sc.add(i);
}
// Add a listener to the stream.
// Now the stream has an assigned listener.
StreamSubscription<int> subscriber = stream.listen(OnValueHandlerContainer.onValue);
OnValueHandlerContainer.setStreamSubscriber(subscriber);
subscriber.onDone(() => print("The subscription is done!"));
print("Does the stream provided by the controller have a listener ? ${sc.hasListener ? 'yes' : 'no'} - the answer should be yes.");
// Wait for 10 seconds.
print("Start waiting for 10 seconds");
Future.delayed(Duration(seconds: 10)).then((var v) => print("10 seconds ellapsed!"));
print("End of script");
}
The result:
Does the stream provided by the controller have a listener ? no - the answer should be no.
Send the value 0 into the stream.
Send the value 1 into the stream.
Send the value 2 into the stream.
Controller: the stream has been assigned a listener!
Does the stream provided by the controller have a listener ? yes - the answer should be yes.
Start waiting for 10 seconds
End of script
onValue: An event has been raised. The associated value is 0!
Pause the subscription. Wait for 1 second. Resume the subscription
Controller: the stream has been paused!
onValue: An event has been raised. The associated value is 1!
Pause the subscription. Wait for 1 second. Resume the subscription
onValue: An event has been raised. The associated value is 2!
Pause the subscription. Wait for 1 second. Resume the subscription
Controller: the stream has been resumed!
10 seconds ellapsed!
Basically, the code provided performs the following actions :
A stream controller is created.
3 events are injected into the stream provided by the controller.
A listener subscribes to the stream provided by the controller.
We assign an "onDone" handler to the listener subscription.
Within the stream listener (OnValueHandlerContainer::onValue) we pause and resume the subscription.
The stream listener fires up 3 times, as expected.
However:
the "onDone" handler is never executed. I expect it to be executed at the end of the script execution, while the controller is being destroyed (and, thus, the subscription gets closed).
the "onPause" and "onResume" handlers fire up only once. I expect them to be executed 3 times.
Any idea ?
The reason you don't get a "done" event is that you never close the stream subscription.
The reason you don't get more "pause" events is that the stream subscription is clever.
The first thing you do is to add a lot of events, before anyone even listens to the stream. You should never do that in real code, instead only start adding events when the onListen is called, and stop again when onPause is called, until the subscription is resumed.
Here, the stream subscription gets stuffed up with a number of events, then it delivers one event, and then the subscription is paused.
The subscription dutifully reports that back to the controller.
Then the subscription gets a resume. This is where it gets clever. Since it already has events to deliver, it does not report the resume back to the controller. It doesn't actually want more events right now, there are plenty to deliver. And so it delivers the buffered events, one at a time, until the buffer is empty. At that point, it reports the resume back to the controller.
The controller reports that work has been resumed, but since nobody adds any more events, and nobody calls close, nothing further will happen.
I'd like to implement websocket reconnect in webapp if internet connection is lost. In order to detect that internet is lost I use ping-pong approach, which means that I send from client ping-message and server returns me pong-message.
When webapp loaded I send init ping message and start to listen a reply on socket some kind of this:
this.websocket.onmessage = (evt) => {
try {
const websocketPayload: any = JSON.parse(evt.data);
if (websocketPayload.pong !== undefined && websocketPayload.pong == 1) {
this.pingPong$.next('pong');
}
It means that internet connection looks ok and we can continue. Also I have the follow code:
Observable.race(
Observable.of('timeout').delay(5000).repeat(),
this.pingPong$
).subscribe((data) => {
console.log("[ping-pong]:", data);
if (data == 'pong') {
Observable.interval(5000).take(1).subscribe(() => {
console.log("[ping-pong]:sending ping")
this.send({ping:1})
});
} else if (data == 'timeout'){
// show reconnect screen and start reconnect
console.error("It looks like websocket connection lost");
}
});
But!
When this.pingPong$ subject stops to emit events - .next() doesn't happen because of we can't get response when I break connection manually - I considered that in Observable.race this observable will be emitted
Observable.of('timeout').delay(5000).repeat()
But my subscribe never happens if this.pingPong$ stop emitting.
Why ?
Thank you
race picks and keeps subscribed to the first Observable that emits.
So if your this.pingPong$ starts emitting and then stops it makes no difference because race keeps subscribed to this.pingPong$. The other Observables don't matter any more. You might want emit one value from this.pingPong$ and the repeat the whole process. For example like the following:
Observable.race(
Observable.of('timeout').delay(5000).repeat(),
this.pingPong$
)
.pipe(
take(1), // complete the chain immediately
repeat() // resubscribe after take(1) completes the chain
)
.subscribe(...);
Obviously it mostly depends on what you want to do but I hope you get the point.
I've tried 2 different ways to setup an observer/observable to make this code work:
Setup #1:
var xObserver;
var xObservable = Rx.Observable
.create(observer => xObserver = observer)
.publish()
.refCount();
Setup #2:
var xObserver = Rx.Subject.create();
var xObservable = x;
Usage
xObserver.next('foo'); // no subscription yet, so nothing should happen
xObservable.subscribe(v => console.log(v)); // pipe values to console
xObserver.next('bar'); // push another value, should go to console
My expectation is for nothing to happen when "foo" is pushed to the observer, and for only "bar" to be shown on the console.
With "Setup #1" I get an error "TypeError: Cannot read property 'next' of undefined" which makes sense because no observer has subscribed yet so the xObserver is not initialized yet.
With "Setup #2" I get an error "TypeError: xObserver.next is not a function".
What am I doing wrong?
Use:
let xObs = new Rx.Subject();
When you use .create you have to supply an Observer, see the docs for details.
Another pitfall might be the version, in version < 5 there is only .onNext():
xObs.onNext("myData");
https://jsfiddle.net/mzkmuewf/
In version > 5, there is only .next():
xObs.next("myData");
https://jsfiddle.net/j1sksg7q/
How can I set the async operator of Observable to run in the main thread instead in another thread. Or at least set to get the result in the main thread once we finish.
#Test
public void retryWhen() {
Scheduler scheduler = Schedulers.newThread();
Single.just("single")
.map(word -> null)
.map(Object::toString)
.retryWhen(ot ->
ot.doOnNext(t -> System.out.println("Retry mechanism:" + t))
.filter(t -> t instanceof NullPointerException && cont < 5)
.flatMap(t -> Observable.timer(100, TimeUnit.MILLISECONDS,scheduler))
.doOnNext(t -> cont++)
.switchIfEmpty(Observable.error(new NullPointerException())))
.subscribeOn(scheduler)
.subscribe(System.out::println, System.out::println);
// new TestSubscriber()
// .awaitTerminalEvent(1000, TimeUnit.MILLISECONDS);
}
I´m trying observerOn and subscribeOn but both are used to set in which thread you want the execution. But in my case I want the execution or the end of it in the same thread where I run the test
Right now the only way to see the prints are just blocking and waiting for the execution.
Regards.
You could use Observable.toBlocking() to get a BlockingObservable and use that to extract your results in your tests.
If you don't specify observeOn/subscribeOn and no operator or Observable changes the thread, then when you subscribe to the observable it will do all the processing during the subscription.
I want to catch errors on the remote page in Casper.
casper.thenEvaluate ->
document.querySelector("#selector-doesnt-exist").attribute-doesnt-exist = 'value'
I have the following listeners set up:
casper.on "page.error", (message, trace) ->
console.log(message)
casper.on "remote.message", (message) ->
console.log(message)
Neither of these is being triggered when the evaluate fails. Is there any way to listen to errors on the remote page?
I don't know how to do this either, and there's probably a more proper way.
Assuming you only want to catch errors in your own evaluate code (not errors on the part of the site you're loading), one solution would be to wrap the casper evaluate function to include a try-catch, and handle the error by returning it from the evaluate:
casper.myevaluate = (fn, args...) ->
s = #evaluate (fn,args) ->
try
fn.apply(this,args)
catch e
return {'ourError':e}
, fn, args
if s.ourError?
ErrorHandler.Exception(s.ourError)
return s
E.g. calling:
casper.myevaluate ()-> silly
...would call your error handler code with ReferenceError: Can't find variable: silly