If I have an array of arrays like this
{
parent: [
{
name: 'stu',
children: [
{name: 'bob'},
{name: 'sarah'}
]
},
{
...
}
]
}
and I want to cycle through each parent and cycle through their children in series, so that I don't start the next parent until all the children have been processed (some long asynchronous process), how do I do this with RxJS?
I have attempted this:
var doChildren = function (parent) {
console.log('process parent', parent.name);
rx.Observable.fromArray(parent.children)
.subscribe(function (child) {
console.log('process child', child.name);
// do some Asynchronous stuff in here
});
};
rx.Observable.fromArray(parents)
.subscribe(doChildren);
But I get all the parents logging out and then all the children.
concatMap works better here. Because if iterating children is async, the order of child will be messed up. concatMap can ensure to finish one parent at a time.
Rx.Observable.from(parents)
.concatMap(function (p) {
return Rx.Observable.from(p.children)
})
.subscribe();
It looks like this was asked a while ago, but here's one way to deal with this scenario:
Rx.Observable.fromArray(parents)
.flatMap(function(parent) {
return parent.children;
})
.flatMap(function(child) {
return doSomeAsyncThing(child); //returns a promise or observable
})
.subscribe(function(result) {
// results of your async child things here.
});
The idea is to leverage flatMap which will take any Arrays, promises or observables returned and "flatten" them into an observable of individual things.
I think you might also benefit from flat mapping the results of your async things you're doing with the child nodes, so I've added that in there. Then you can just subscribe to the results.
I still feel like this question is lacking some context, but hopefully this is what you're looking for.
Related
Let's say I have an interval that each second sends an heartbeat. At each beat i'd like to inspect something on my web page and react accordingly. I'd also like the option to unsubscribe from the inner Observables actions, but keep getting the heartbeat so when i subscribe back, everything will flow as before.
Creating a Subscription from Interval and piping it leaves no option to unsubscribe from the inner action, but only the whole subscription as whole.
Is there a way to return the inner Observable so i can unsubscribe from it while still retaining the heartbeat created from the Interval?
Edit: I've tried to create a class to describe what I'm talking about:
class Monitor {
sub: Subscription | null = null;
start() {
this.sub = this.monitor().subscribe();
}
monitor() {
const dom$ = someSelectorObserver(this.win.document, '#someSelector').pipe(
mergeMap(newElementOrBail => {
if (newElementOrBail) {
return handle(newElementOrBail);
} else {
return bail();
}
}),
tap({
error: error => this.log.error(error),
}),
);
return dom$;
}
handle(ele: HTMLElement) {
// do stuff
}
bail() {
this.sub.unsubscribe();
}
}
So basically my monitor starts with creating the subscription, as long as there's a new element to handle everything is fine, but when a bail signal appears I'd like to unsubscribe while still monitoring the DOM changes for a return of the previous elements.
So the outer subscription is basically the DOM observer and the inner is the mergeMap handle function. Does it make more sense?
You could just put some conditional on your inner observable:
private takeSignal = true
interval(3000).pipe(switchMap(() => takeSignal ? inner$ : NEVER))
Then just flip takeSignal as needed.
But it seems easier to just unsubscribe from the whole thing and resubscribe when needed. Why keep the interval going when you’re not using it?
You can split your logic in two (or more) streams.
Store heartbeat$ in a separate variable and subscribe to multiple times for different reasons.
In this way, you'd be able to split your logic into different streams and control subscriptions individually.
const heartbeat$ = interval(3000);
const inspectWeb = heartbeat$.pipe(
// do stuff
).subscribe()
inspectWeb.unsubscribe()
heartbeat$.pipe(
// do other stuff
).subscribe()
I got to refactor some code for a tremendous angular project I'm newly involved in. Some parts of the code lake of RxJS operators and do things like simple nested subscribes or copy/paste error handlers instead of using pipes.
Is it safe to assume that any simple 1 depth nested subscribes can be replaced by a mergeMap?
Let's take a login method like this :
private login() {
this.userService.logIn(this.param1, this.param2).subscribe((loginResult: {}) => {
this.userService.getInfo(this.param3).subscribe((user: UserModel) => {
// [the login logic]
},
(e) => {
// [the error handling logic]
})
}, (e) => {
// [The exact same copy/pasted error handling logic]
});
}
Is it safe to replace it with this?
private login() {
this.userService.logIn(this.param1, this.param2)
.pipe(
mergeMap((x) => this.userService.getInfo(this.param3))
)
.subscribe((user: UserModel) => {
// [the login logic that redirects to "my account" page]
},
(e) => {
// [the error handling logic]
});
}
What would be the difference with flatMap or switchMap here, for instance?
For your case I will go with SwitchMap.
mergeMap/FlatMap: transforms each emitted item to a new observable as defined by a function. Executes the executions in parallel and merges the results (aka flatMap) order doesn't matter.
swicthMap: transforms each emitted item to a new observable as defined by a function.
-Subscribers from prior inner observable.
-Subscribers to new inner observable.
-Inner observable are merged to the output stream.
-When you submit a new observable, multiple times the previous one gets cancelled and only emits the last one which is awesome for performance.
What way is better? I think than first way is better. Import parent in child looks weird to me, but maybe I'm wrong.
RootStore:
export const RootStore = types
.model('RootStore', {
store1: types.optional(Store1, {}),
store2: types.optional(Store2, {}),
store3: types.optional(Store3, {}),
store3: types.optional(Store4, {}),
name: 'name'
})
export const rootStore = RootStore.create()
First way:
export const Store1 = types
.model('Store1', {
some: ''
})
.views(self => ({
get rootStore() {
return getParent(self)
},
get name() {
return self.rootStore.name
}
}))
Second way:
import { rootStore } from './rootStore'
export const Store1 = types
.model('Store1', {
some: ''
})
.views(self => ({
get name() {
return rootStore.name
}
}))
All answers to this question are likely to be opinionated..
If you are going to do this, I think the first way is better. Just because it means the child does not need to know anything about it's parent other than that it exposes a name property.
That being said, I'm really not a big fan of either approaches.
Whether you use getParent or a closure, this encourages a coupling of the two models. This results in decreased modularity and harder testing since every Store1 must be a child of a RootStore to function properly.
I think a better approach would be to remove the dependency between child->parent. However, if you are purposefully making use of the tree structure that MST provides, my suggestion might be better in theory than practice.
The simplest approach to removing the dependency is to have the caller of Store1's actions/views pass whatever data is needed in as parameters. Once again, this does not always make sense in practice.
If all you need is access the root node in the tree, then there's a dedicated helper function specifically for that case - getRoot(self).
Given an object in a model tree, returns the root object of that tree.
I have an SPA that is loading some global/shared data (let's call this APP_LOAD_OK) and page-specific data (DASHBOARD_LOAD_OK) from the server. I want to show a loading animation until both APP_LOAD_OK and DASHBOARD_LOAD_OK are dispatched.
Now I have a problem with expressing this in RxJS. What I need is to trigger an action after each DASHBOARD_LOAD_OK, as long as there had been at least one APP_LOAD_OK. Something like this:
action$
.ofType(DASHBOARD_LOAD_OK)
.waitUntil(action$.ofType(APP_LOAD_OK).first())
.mapTo(...)
Does anybody know, how I can express it in valid RxJS?
You can use withLatestFrom since it will wait until both sources emit at least once before emitting. If you use the DASHBOARD_LOAD_OK as the primary source:
action$.ofType(DASHBOARD_LOAD_OK)
.withLatestFrom(action$.ofType(APP_LOAD_OK) /*Optionally*/.take(1))
.mapTo(/*...*/);
This allows you to keep emitting in the case that DASHBOARD_LOAD_OK fires more than once.
I wanted to avoid implementing a new operator, because I thought my RxJS knowledge was not good enough for that, but it turned out to be easier than I thought. I am keeping this open in case somebody has a nicer solution. Below you can find the code.
Observable.prototype.waitUntil = function(trigger) {
const source = this;
let buffer = [];
let completed = false;
return Observable.create(observer => {
trigger.subscribe(
undefined,
undefined,
() => {
buffer.forEach(data => observer.next(data));
buffer = undefined;
completed = true;
});
source.subscribe(
data => {
if (completed) {
observer.next(data);
} else {
buffer.push(data);
}
},
observer.error.bind(observer),
observer.complete.bind(observer)
);
});
};
If you want to receive every DASHBOARD_LOAD_OK after the first APP_LOAD_OK You can simply use skipUntil:
action$ .ofType(DASHBOARD_LOAD_OK)
.skipUntil(action$.ofType(APP_LOAD_OK).Take(1))
.mapTo(...)
This would only start emitting DASHBOARD_LOAD_OK actions after the first APP_LOAD_OK, all actions before are ignored.
Is there any differences between
Observable.pipe(take(1)).subscribe(...)
vs
const subscription = Observable.subscribe(() => {
// Do something, then
subscription.unsubscribe()
})
The take(1) approach has a number of advantages over subscribe:
Code readability (and elegance).
The second approach requires that you hold and manage extra variables.
The second approach will not invoke the complete handler. This is because .take(1) actually create a new observable which potentially yields a single item and completes.
The second approach will work for the trivial case of taking a single element, but if you need to take more then 1, take(4) will stay simple while the second approach will become hard to code.
The 3rd item is the rxjs related one, the others relate to coding style.
Have a look at a sample here.
In Angular2, I find myself using both paradigms.
The first makes the most sense inside of a method, where as the second is better used in a constructor, with a cleanup in the deconstructor.
doThing(){
this.store.select('thing').pipe(take(1))
.subscribe(item => {
otherMethod(item)
});
}
vs
class SomeClass{
public val;
private sub;
constructor(){
this.sub = this.store.select('thing')
.subscribe(item => {
this.val = item
});
}
ngDestroy() {
this.sub.unsubscribe()
}
}