Folding two callbacks into one Observable - mongodb

The snippet of code below is functional (in the sense that it's working ;-)), but seems lame at best and well...
Can anyone suggest a way to make this more composable or at least less ugly?
The code is based on the examples on this page:
Wrap an Existing API with RxJS
function connect() {
return rx.Observable.create(function (observer) {
mongo.connect('mongodb://127.0.1:27017/things', function(err, db) {
if(err) observer.onError(err);
observer.onNext(db);
});
}).publish().refCount();
}
function getThings(db) {
return rx.Observable.create(function (observer) {
db.collection('things').find().toArray(function(err, results) {
if(err) observer.onError(err);
observer.onNext(results);
observer.onCompleted();
});
return function () {
db.close();
};
}).publish().refCount();
}
connect().subscribe(
function (db) {
getThings(db).subscribe(console.log);
}, function (err) {
console.log(err);
}
);

In this specific example, assuming that getThings() is supposed to happen only once after connect() happens, I would change the implementation of getThings() as such:
function getThings() {
return connect()
.flatMap(function(db) {
return rx.Observable.create(function (observer) {
db.collection('things').find().toArray(function(err, results) {
if(err) observer.onError(err);
observer.onNext(results);
observer.onCompleted();
});
return function () {
db.close();
};
});
});
}
Then you can just subscribe to the getThings() stream:
getThings().subscribe(console.log);
We used flatMap to hide the the connection step inside the whole getThings(). FlatMap's documentation sounds complicated, but it isn't that complicated. It just substitutes an event from the source Observable with another future event. Explained in diagrams, it substitutes each x event with a future y event.
---x----------x------->
flatMap( x => --y--> )
------y----------y---->
In our case, x event is "connected successfully", and y is "got 'things' from database".
That said, there are a couple of different ways of doing this, depending on how the app is supposed to work. It is better to think of RxJS as "Event Bus on steroids" rather than a replacement for chainable Promises, because it really is not the latter.
Developing on RxJS is best if you model "everything that happens in the app" as streams of events. If done properly, you shouldn't see these chainable "do this, then do that, then do that", because ultimately that's an imperative paradigm, and RxJS is capable of more than that. Ideally it should be more about telling what the events are, in a declarative fashion. See this tutorial for more explanations, specially the discourse in the "Wrapping up" section. Also this gist might help.

Related

How to execute function after stream is closed in Dart/Flutter?

So basically I am using the flutter_uploader package to upload files to a server and I'd like to execute a function after the upload is complete:
final StreamSubscription<UploadTaskProgress> subscription = _uploader.progress.listen(
(e) {
print(e.progress);
},
onError: (ex, stacktrace) {
throw Exception("Something went wrong updating the file...");
},
onDone: () {
myFunction(); // won't run
},
cancelOnError: true,
);
The problem is the onDone function doesn't execute thus meaning myFunction never executes. I've done some digging and I found that onDone gets called when we close the stream but there is no such method on the subscription variable. I have not used streams much and therefore am pretty bad with them.
My question is, how can I run myFunction? once the stream is complete? I thought that onDone would get called when such is the case but I guess not.
Thank you!
I didn't used that package before but I was reading a litle bit about the package and I think you can execute your funciton inside the main block, the other ones are to handle internal processes like stopping a background job or some other external stuff like notify the error to some error monitoring tool, this is what I propose to you:
final StreamSubscription<UploadTaskProgress> subscription =
_uploader.progress.listen(
(e) {
if (e.status is UploadTaskStatus._internal(3)) {
myFunction()
}
print(e.progress);
},
onError: (ex, stacktrace) {
throw Exception("Something went wrong updating the file...");
},
cancelOnError: true,
);
Just to be clear I'm not sure of the specific implementation, is just and idea I get from the docs, seems like the event also contains an status property which has a constant for when the event is completed
https://pub.dev/documentation/flutter_uploader/latest/flutter_uploader/UploadTaskProgress/UploadTaskProgress.html
https://pub.dev/documentation/flutter_uploader/latest/flutter_uploader/UploadTaskStatus-class.html
Hope this helps you :D

IONIC-V3 : Wait page to pop before continue executing code

I’m iterating over a JSON that contains some rules to build my page. The loop is something like this:
flux.forEach(element => {
this.navCtrl.push(element.pageName);
});
My issue here is that I need to wait for this page to execute its action before call the next, this loop makes a stack. How can I make sort of a promise to wait the page to execute its duty before continue the loop?
Thank you all!
To solve promises in sequence, you can use reduce() as explained here.
element.reduce((promise,item) => {
return promise.then(() => {
return new Promise((resolve, reject)=> {
this.navCtrl.push(element.pageName);
resolve();
})
})
},Promise.resolve())

how to find all the data in a Flux(Parent) are processed by its inner non-blocking Flux or Mono(child)?

I have a aggregator utility class, where i have to joint more than one cassandra table data. my production code will looks like below but not exactly same.
#Autowired FollowersRepository followersRepository;
#Autowired TopicRepository topicRepository;
#GetMapping("/info")
public Flux<FullDetails> getData(){
return Flux.create(emitter ->{
followersRepository.findAll()
.doOnNext(data -> {
List<String> all = data.getTopiclist(); //will get list of topic id
List<Alltopics> processedList = new ArrayList<Alltopics>();
all.forEach(action -> {
topicRepository.findById(action) //will get full detail about topic
.doOnSuccess(topic ->{
processedList.add(topic);
if (processedList.size() >= all.size()) {
FullDetails fulldetails = new FullDetails(action,processedList);
emitter.next(fulldetails);
//emitter.complete();
}
})
.subscribe();
});
})
.doOnComplete(() ->{
System.out.println("All the data are processed !!!");
//emitter.complete(); // executing if all the data are pushed from database not waiting for doOnNext method to complete.
})
.subscribe();
});
}
For more details, refer the code here CodeLink.
I have tried with doOnComplete and doOnFinally for outer Flux, it is not waiting for all inner Non-blocking calls to complete.
I want to call onComplete, after processing all the nested Mono/Flux(non-blocking) request inside Flux.
For nested blocking flux/mono, the outer flux doOnComplete method is executing after completion of inner Flux/Mono.
PostScript(PS):-
In below example, i am not able find where to place emitter.complete().
because doOnComplete() method is called before completion of all the inner Mono.
Request Body:-
[{ "content":"Intro to React and operators", "author":"Josh Long", "name":"Spring WebFlux" },{ "content":"Intro to Flux", "author":"Josh Long", "name":"Spring WebFlux" },{ "content":"Intro to Mono", "author":"Josh Long", "name":"Spring WebFlux" }]
My Rest Controller:-
#PostMapping("/topics")
public Flux<?> loadTopic(#RequestBody Flux<Alltopics> data)
{
return Flux.create(emitter ->{
data
.map(topic -> {
topic.setTopicid(null ==topic.getTopicid() || topic.getTopicid().isEmpty()?UUID.randomUUID().toString():topic.getTopicid());
return topic;
})
.doOnNext(topic -> {
topicRepository.save(topic).doOnSuccess(persistedTopic ->{
emitter.next(persistedTopic);
//emitter.complete();
}).subscribe();
})
.doOnComplete(() -> {
//emitter.complete();
System.out.println(" all the data are processed!!!");
}).subscribe();
});
}
Here are a few rules that you should follow when writing a reactive pipeline:
doOnXYZ operators should never be used to do lots of I/O, latency involved operations or any reactive operation. Those should be used for "side-effects" operations, such as logging, metrics and so on.
you should never subscribe from within a pipeline or a method that returns a reactive type. This decouples the processing of this operation from the main pipeline, meaning there's no guarantee you'll get the expected result at the right time nor that the complete/error signals will be known to your application.
you should never block from within a pipeline or a method that returns a reactive type. This will create critical issues to your application at runtime.
Now because your code snippet is quite convoluted, I'll just give you the general direction to follow with another code snippet.
#GetMapping("/info")
public Flux<FullDetails> getData(){
return followersRepository.findAll()
.flatMap(follower -> {
Mono<List<Alltopics>> topics = topicRepository.findAllById(follower.getTopiclist()).collectList();
return topics.map(topiclist -> new FullDetails(follower.getId(), topiclist));
});
}

ngrx/effects - How to test with a promise

I'm using ngrx/effects with marbles testing. I have a service that uses promises. I want my effect to call the service and handle both successful and error states. I have code like this
Effect:
.mergeMap(() =>
this.service.getThings()
.map((things) => new SetThingsAction(things))
.catch((error) =>
of(new HandleAPIErrorAction(error))
)
)
.catch((error) =>
of(new HandleAPIErrorAction(error))
);
Service:
public geThings() {
return Observable.fromPromise(this.promiseBasedThing.getTheThings());
}
Then a test:
actions = hot("a", { a: new GetThingsAction() });
const response = cold("-#", {});
service.getThings.and.returnValue( response );
const expected = cold("-b", { b: new HandleAPIErrorAction("error") });
expect(effects.getThings$).toBeObservable(expected);
This actually all works. However the double catch in the effect seems clearly bad and probably suggests I don't understand how Observables work. In the real world only the later catch is effective. In a test the first is effective.
Based on this it seems like Promises don't work with marbles tests. This SO question gives an idea on error handling but it seems impossible to test because it has a Promise.
How can I use ngrx/effects with error handling, promises, and testing.
Can answer my own after further research.
https://jsfiddle.net/btroncone/upy6nr6n/
Basically I needed to do the catch in the getThings instead of in the effect.
getThings() {
return Observable.fromPromise(this.promiseBasedThing.getTheThings())
.catch((error) => Observable.of(error));
}
Also learned that it's much easier to solve these problems with a simple rsjx example instead of trying to solve it while using ngrx/effects too. This still has two catch statements, but the test mocking now matches how it works in reality.

What should be enqueued in Protractor ControlFlow explicitly?

As it is written in Protractor ControlFlow documentation - WebDriver async calls are automatically stored in Control Flow and will be executed in the defined order. In reality it seems that such approach is just a syntax sugar to avoid explicitly written "then" chains. But when I need to put my async function into Control Flow queue explicitly? Imagine that I have a pieced of code:
myAsync(xxx).then(function() {
doSomething();
return;
});
and this code is in the middle of Protractor/Jasmine test so there are asserts above it and below; Should I do something like:
flow.execute(myAsync);
and if yes where I must put my "then" section in this case?
it('blah', function() {
browser.get('something');
expect(element('foo').getText()).toBe('bar');
var myAsync = function() {
// if your async function doesn't return a promise, make it one
var deferred = protractor.promise.defer()
// do some async stuff in here and then reject or fulfill with...
if (error) {
deferred.reject(error)
else {
deferred.fulfill(value);
}
return deferred.promise;
};
// hook into the controlFlow and execute the async thing so you can check after
browser.controlFlow().execute(myAsync);
expect(element('foo').getText()).toBe('baz');
// or check the return value of the promise
browser.controlFlow().execute(myAsync).then(function(result) {
expect(result).toBe('something');
});