Convert a stream of Promises into a stream of values - promise

I am somewhat new to RxJs and I am trying to mix the world of promises and observables.
Here is what I want:
I have an observable (call it clickObs) which listens to a click and as a result interrogates a database, producing a promise which resolves to a value when the database querying concludes (successfully).
My observable thus generates a stream of promises from a stream of clicks, and what I want is to generate from that observable, a stream of corresponding resolved values.
From past questions on stackoverflow, I read about defer, flatMap, mergeAll, and fromPromise, but cannot get my head around how to articulate the four to solve my problem.
Any suggestions?

You shouldn't need all four, just look at flatMap or its sibling flatMapLatest
clickObs.flatMapLatest(function() {
//Access the db and return a promis
return database.query(queryObj);
})
.subscribe(function(result) {
//Result is implicitly flattened out
/*Do something with the result*/
});
flatMap will implicitly convert a promise or array-like object into an Observable and flatten out the resulting sequence. FlatMapLatest is similar but will ignore old events, if a newer one arrives before the previous one completed.

Related

withLatestFrom using two Redux actions

I have been trying to use rxjs properly with Redux observable and Redux actions. I've tried many different combinations of calling the below functions and every possible combination has given me different errors.
Basically, I have two actions that can be dispatched in my app: SET_CIRCULAR_DATA and SET_MODAL_OVER_DEEP_LINK_FLAG.
I'd like another action to be dispatched, determineFoundInProducts() when SET_MODAL_OVER_DEEP_LINK_FLAG happens followed by the first (successful) SET_CIRCULAR_DATA action. Any subsequent SET_CIRCULAR_DATA actions should be ignored until SET_MODAL_OVER_DEEP_LINK_FLAG happens again. Because of this need, I felt withLatestFrom() made the most sense. I also thought that takeUntil() and repeat() might be helpful to help me accomplish what I need, though I'm not including them in this code example because I'm stuck at the withLatestFrom() part.
I've tried different combinations of piping, not piping, using switchMap, map, mapTo, but nothing seems to work. I am importing all the used rxjs functions and I'm combining epics at the root, so it couldn't be that. I just can't seem to get the right combination to not get this to error.
Some examples of errors are
Actions must be plain objects. Use custom middleware for async actions.
You provided 'undefined' where a stream was expected
I am using RXJS ^5.5.6 and Redux Observable ^0.17.0.
import 'rxjs';
import { Observable } from 'rxjs/Observable';
import { withLatestFrom, map, tap, mergeMap } from 'rxjs/operators';
import ....all the types and actions
export const handleProductDetailsModalOnLoginRedirect = (action) =>
action.ofType(weeklyAdTypes.types.SET_CIRCULAR_DATA).pipe(
withLatestFrom(
action.ofType(navigationTypes.types.SET_MODAL_OVER_DEEP_LINK_FLAG)
),
mergeMap(([first, second]) => {
console.log(first, second);
return determineFoundInProducts();
})
);
EDIT
I was struggling in my original post to determine what part of the code was breaking. What I had trouble with was that the redux observable action could not communicate with the rxjs functions. What I found was that piping appropriately piped (pun intended) the action to be workable with rxjs functions. Here is what I did.
action.ofType(navigationTypes.types.INIT_REDIRECT_MODAL_LOGIC)
.switchMap(() =>
action.ofType(weeklyAdTypes.types.SET_CIRCULAR_DATA)
.withLatestFrom(action.ofType(navigationTypes.types.SET_MODAL_OVER_DEEP_LINK_FLAG))
.take(1)
.pipe(
map(([first]) => {
// Do stuff
return determineFoundInProducts();
}))
)
The answer depends on what you want to happen if multiple SET_MODAL_OVER_DEEP_LINK_FLAG actions are dispatched before any SET_CIRCULAR_DATA action is, as well as whether they should some how be paired together with some sort of unique ID or not.
It sounds most likely like you're looking for exhaustMap.
The exhaustMap operator is basically the opposite behavior of switchMap. It maps inputs to inner observable and flattens its output, ignore other input values until that observable completes. It exhausts the inner Observable.
In your use case it means that we first listen for SET_MODAL_OVER_DEEP_LINK_FLAG, then we listen for a single SET_CIRCULAR_DATA, but while we're waiting for that subsequent SET_CIRCULAR_DATA we ignore all possibly incoming SET_MODAL_OVER_DEEP_LINK_FLAG actions. Note that the take(1) is important, because otherwise we'll listen for a stream of every SET_CIRCULAR_DATA, not just one.
export const handleProductDetailsModalOnLoginRedirect = (action) =>
action.ofType(navigationTypes.types.SET_MODAL_OVER_DEEP_LINK_FLAG)
.exhaustMap(() =>
action.ofType(weeklyAdTypes.types.SET_CIRCULAR_DATA)
.take(1)
.map(() => determineFoundInProducts())
);
Btw it's common to ask Redux Observable questions that are actually just RxJS questions. This is a cool thing to note because the RxJS community is massive so you'll find a TON more resources on it, as well as have a better chance of getting help if you're able to rephrase questions to be agnostic of Redux Observable--e.g. change ofType('TYPE') to a filter(d => d.type === 'TYPE')
The above answer will definitely help others. However, what I was struggling with was having redux observable action communicate with RXJS functions. Please see my edit to my original post for solution.

RxJS 5.5.0-beta : mergeMap behavior differing from map + mergeAll

I am having trouble creating a reproducible example so bear with me.
I have a library I created that wraps async API responses in an Observable. Simplifying greatly, the library has functions like lib.ask("end-point") that return an Observable with the response of the API call to that end point.
I want to run that API call every time another event happens. I'm attempting to model that with some version of mergeMap/switchMap/concatMap, depending on the situation. Let's use mergeMap as an example.
If I use map + mergeAll, it works just fine. I am writing it like so:
var responses$ = event$.pipe(
map(()=>lib.ask("end-point")),
mergeAll()
);
When I subscribe to responses$, I get my response!
However, if I rewrite the same chain with mergeMap instead, it does not work:
var responses$ = event$.pipe(
mergeMap(()=>lib.ask("end-point"))
);
In this case, when I subscribe I do not get any results. It almost appears as if the inner subscription never gets made.
Like I said, I am struggling to create a reproducible example. I have attempted to debug what RxJS does to try to find where this thing is failing, but have not been able to figure it out. If anyone can think of why or in what situations mergeMap would behave differently than map + mergeAll, that might help me figure out where my problem is.

Rxjs 5: How to establish chain of Observable?

I'm novice to RxJS, I'm trying to implement chain of observable that behave exactly like MS-Excel. The concept: Lets assume the excel have 5 columns 'Name', 'Age', 'Sex', 'Country', 'Zipcode'. We can apply filter on each column independently that also affect the records show in the other columns.
Here the data-source receives the data back-end service, the data-source will have only two functions "addRecord" & "removeRecord".
How I'm trying to achieve here lets say I will create Observable and attach to the data-source call it as OBS-1 this will receive data from data-source. The OBS-1 can have its own filters. Lets say I will create another Observable OBS-2 which will receive data OBS-1 (filtered data if any filters in OBS-1). Another Observable say OBS-3 which again receive data from OBS-2 (filtered if any in OBS-2), so on.
If OBS-2 is destroyed (unsubscribed) the OBS-3 will receive the data from OBS-1.
How do we achieve this in the RxJs?
I think you misunderstood a few thing about Rx. Observables do not have filters and you do not 'live' add and remove filters from them. Neither do observables forward data based on who is subscribed.
Instead, you build up a call chain. You start with a source observable, like one from the addRecord and one from the removeRecord event. You then chain these observables to form new observables trough various operators in Rx and eventually you subscribe to the final observable. Subscribing will activate the entire chain and when the source events fire, all operators will trigger and eventually the event will (if not filtered) reach subscribe.
You can actually do the thing you describe in Rx. Changing a filter on an observable for example can be done relatively easy with switchMap, an operator that let you project a sequence onto another and switch over to the new sequence each time. For example filterSource.switchMap(filterFunction => Obs-1.filter(filterFunction)). Even simpler than this, you could just unsubscribe the first subscription and set up the Rx chain again. Using the build in functions however leaves a lot of juggling state out of the equation.
However, i strongly suspect you do not actually need behavior that is this complicated. What you want can be archived simply like this:
var Src-1 = fromEvent(dataSource, 'addRecord') // create the first source
var Src-2 = fromEvent(dataSource, 'removeRecord') // and the other source
var Obs-1 = Src-1.combineLatest(Src-2) // combine both sources
.filter(e => someCondition(e)) // filter the source
var Obs-2 = Obs-1.mergeMap(e => someOtherCondition(e) ? Change(e) : Rx.Observable.of(e)) // on someOtherCondition, either transform the source with the `Change(e)` function. Or keep it unchanged with `of(e)`
var Obs-3 = Obs-2.filter(e => anotherCondition(e)) // Filter again
var sub = Obs-3.subscribe() // activate the sequence.

Split node.js event stream based on unique id in event body

I have a node.js-based location service that produces events as users move about, and I want to use RxJS to process these events and look for arbitrarily complex patterns like a user enters a region and visits 2 points of interest within 1 hour.
To begin with I need to split the stream of events base on unique user ids (from event body), but I am not finding any stock RxJS functions that will do this.
filter() would require that all uuids be known beforehand which is not desirable.
groupBy() looks like it would need to process the entire sequence prior to returning the grouped observables, which is not possible.
I'm thinking that maybe I need to build a custom observable that maintains a map of uuids to observables, and instantiate new observables as required. Each of these observables would then need to undergo identical processing in search of the pattern match, and ultimately trigger some action when a user's movements match the pattern. One of the obvious challenges here is I have a dynamically growing map of observables being produced as user enter the system and move about.
Any ideas how something like this could be achieved with RxJS?
I think you are misunderstanding how groupBy() works. It will generate a new Observable every time a new key is generated, and if the key already exists, it will just be pushed to the existing Observable.
So for your problem it should look something like this:
var source = getLocationEvents();
var disposable = new Rx.CompositeDisposable();
disposable.add(
source.groupBy(function(x) { return x.userid; })
.map(function(x) {
return x.map(function(ev) { /*Process the the event*/ });
})
.subscribe(function(group) {
disposable.add(group.subscribe(/*Do something with the event*/));
});

Create one-time subscription

I need to create a subscription to an Observable that is immediately disposed of when it is first called.
Is there something like:
observable.subscribeOnce(func);
My use case, I am creating a subscription in an express route handler and the subscription is being called multiple times per request.
Not 100% certain about what you need, but if you only want to observe the first value, then use either first() or take(1):
observable.first().subscribe(func);
note: .take(1) and .first() both unsubscribe automatically when their condition is met
Update from RxJS 5.5+
From comment by Coderer.
import { first } from 'rxjs/operators'
observable
.pipe(first())
.subscribe(func);
Here's why
RxJS has some of the best documentation I've ever come across. Following the bellow link will take you to an exceedingly helpful table mapping use cases to operators. For instance, under the "I want to take the first value" use case are three operators: first, firstOrDefault, and sample.
Note, if an observable sequence completes with no notifications, then the first operator notifies subscribers with an error while the firstOrDefault operator provides a default value to subscribers.
operator use case lookup
UPDATE(DEC/2021):
As toPromise() function has been deprecated in RxJS 7, new functions have been announced to be used instead of it. firstValueFrom and lastValueFrom.
firsValueFrom function resolves the first emitted value and directly unsubscribe from the resource. It rejects with an EmptyError when the Observable completes without emitting any value.
On the other hand, lastValueFrom function is, to a certain degree, same to toPromise() as it resolves the last value emitted when the observable completes. However, if the observable doesn't emit any value, it will reject with an EmptyError. Unlike toPromise() which resolve undefined when no value emits.
For more information, please check the docs.
Old Answer:
If you want to call an Observable only one time, it means you are not going to wait for a stream from it. So using toPromise() instead of subscribe() would be enough in your case as toPromise() doesn't need unsubscription.
To supplement #Brandon's answer, using first() or the like is also essential for updating a BehaviorSubject based on its Observable. For example (untested):
var subject = new BehaviorSubject({1:'apple',2:'banana'});
var observable = subject.asObservable();
observable
.pipe(
first(), // <-- Ensures no stack overflow
flatMap(function(obj) {
obj[3] = 'pear';
return of(obj);
})
)
.subscribe(function(obj) {
subject.next(obj);
});
Clean and Convenient Version
Expanding on M Fuat NUROĞLU's amazing answer on converting the observable to a promise, here's the very convenient version of it.
const value = await observable.toPromise();
console.log(value)
The beauty of this is that we can use that value like a normal variable without introducing another nested block!
This is especially handy when you need to get multiple values from multiple observables. Neat and clean.
const content = await contentObservable.toPromise();
const isAuthenticated = await isAuthenticatedObservable.toPromise();
if(isAuthenticated){
service.foo(content)
}
Of course, you will have to make your containing function async if you are to go with this route. You can also just .then the promise if you don't want the containing function to be async
I'm not sure if there are tradeoffs with this approach, feel free to let me know in the comments so we are aware.
P.S. If you liked this answer, don't forget to upvote M Fuat NUROĞLU's Answer as well :)
I had similar question.
Below was called even later on from different state changers. As I did not want to.
function foo() {
// this was called many times which was not needed
observable.subscribe(func);
changeObservableState("new value");
}
I decided to try unsubscribe() after subscribe as below.
function foo() {
// this was called ONE TIME
observable.subscribe(func).unsubscribe();
changeObservableState("new value");
}
subscribe(func).unsubscribe(); is like subscribeOnce(func).
I hope that helped you too.
observable.pipe(take(1)).subscribe() use take 1 it subscribe for one time then exit

Resources