I want a variable states$: an observable stream of objects, with each object containing a member nextState$ of type observable. This property nextState$ sends a unique item corresponding to the next state, and so on...
example:
const states$ = of({ nextState$: createObservableWithNextState$() }).pipe(
switchMap(state => state.nextState$),
switchMap(state => state.nextState$),
switchMap(state => state.nextState$),
switchMap(state => state.nextState$),
...
)
of course it doesn't work, for two reasons at least:
I don't want the ... infinite repetition of switchMap in my code
I want to subscribe to state$ and receive each object (including the initial one in the of())
Of course I could create my own observable from scractch but before I would like to known if it would be possible with existing rxjsoperators. Any idea ?...
RxJS#expand
Expand should do what you're after pretty simply.
I assume at some point you'll reach a state without a nextState$, but you can change that condition easily.
const states$ = of({
nextState$: createObservableWithNextState$()
}).pipe(
expand(state => state.nextState$ != null? state.nextState$ : EMPTY)
);
Expand is closer to mergeMap than switchMap. You can set concurrent to 1 to make it work like concatMap. If you're really after a switchMap-like behaviour, this gets a bit more complicated.
Related
I got 3 endpoints that returns upcoming, current, past events. I should show only the one that is the farthest in the future. To optimize the calls and not to call all the endpoints at once.I have written a simple RxJs stream where I call the first endpoint and if it does not return data I call second and so on. The code looks like this:
this.eventsService.getUpcoming(id).pipe(
switchMap((upcoming) => {
if (upcoming.length) {
return of(upcoming);
}
return this.eventsService.getCurrent(id).pipe(
switchMap((current) => {
if (current.length) {
return of(current);
}
return this.eventsService.getPast(id)
})
);
}),
// some other pipe operators map etc.
It is possible not to have nested switch map within a switch map?
I think you could use just concat() to make the calls sequential and then take(1) and skipWhile() to automatically complete when the first useful response arrives:
concat(
this.eventsService.getUpcoming(id),
this.eventsService.getCurrent(id),
this.eventsService.getPast(id)
).pipe(
skipWhile(response => response.length === 0),
defaultIfEmpty([]),
take(1),
);
take(1) will complete the chain when the first item in skipWhile doesn't match the condition.
Try something like this
this.eventsService.getUpcoming(id).pipe(
switchMap((upcoming) => {
if (upcoming.length) {
return of(upcoming);
}
return this.eventsService.getCurrent(id)
},
switchMap((current) => {
if (current.length) {
return of(current);
}
return this.eventsService.getPast(id)
})
)
This way you do not nest the switchMap
You can use concat operator to create an observable which sequentially emits values from each observable. Pipe the results to the find operator that will return the result from the first result that meets the condition and complete the observable. This will prevent subsequent observables to be executed from the stream created by concat.
Difference between first and take
One side effect of find that I think you will find useful for your example is that if no conditions are met, then the last result is still emitted. This is different then using an operator like first which will throw an error if the source observable completes without a match, or take which won't emit anything since a prior operator would be used for filtering emissions.
So in your case you'll at least get an empty array if all responses are empty.
concat(
// try each request in order.
this.eventsService.getUpcoming(id),
this.eventsService.getCurrent(id),
this.eventsService.getPast(id)
).pipe(
// emits the first result that meets the condition.
find(response => response.length > 0)
);
I have multiple components using this service and I want to debounceTime when I'm piping in the same value but if it's a different value, let it through. In this code, I want to somehow check if a types been used before
from(types).pipe(
debounceTime(1000),
map(([data, type]) => {
// mapping logic
}),
)
It very similar to this issue https://github.com/lodash/lodash/issues/2403 and I'm trying to understand it but I'm hoping to used RXJS code still
Thanks
One possible solution could be to use the pairwise operator to emit both the previous and current values, then use the debounce operator to compare the values and debounce accordingly:
from(types).pipe(
pairwise(),
debounce(([prev, cur]) => (prev === cur)
? timer(1000)
: EMPTY
),
map(([, cur]) => cur), // No further need for prev value
map(([data, type]) => {
// mapping logic
}),
)
The angular material documentation app has the following snippet in it:
// Combine params from all of the path into a single object.
this.params = combineLatest(
this._route.pathFromRoot.map(route => route.params),
Object.assign);
TSLint strikesout combineLatest with this message:
#deprecated — resultSelector no longer supported, pipe to map instead
How should that be fixed?
Also if you understand the technique being used, a simple example with elaboration would be awesome.
Here's a code link:
https://github.com/angular/material.angular.io/blob/master/src/app/pages/component-category-list/component-category-list.ts
combineLatest(observables, resultSelector)
can often be replaced with
combineLatest(observables).pipe(
map(resultSelector)
)
But whether this works in the same way depends on what parameters your resultSelector accepts. combineLatest(observables) emits an array and RxJs automatically spreads this array when you're using the deprecated resultSelector.
return isArray(args) ? fn(...args) : fn(args); // fn is your resultSelector
As Object.assign returns different values depending on whether you provide an array or multiple values you have to spread the array manually.
combineLatest(observables).pipe(
map(items => Object.assign({}, ...items))
)
The result selector is just a function that can be used to change the shape of the output.
So, you can simply omit the result selector param and use a map to transform the shape:
this.params = combineLatest(
this._route.pathFromRoot.map(route => route.params)
).pipe(
map(Object.assign)
);
A common use the of the result selector (for me anyway) was to return the array as an object with named properties, so something like this:
// Old Way
myObs$ = combineLatest(
[src1$, src2$, src3$],
([src1, src2, src3]) => ({src1, src2, src3})
);
// New Way
myObs$ = combineLatest(src1$, src2$, src3$)
.pipe(
map(([src1, src2, src3]) => ({src1, src2, src3}))
);
pipe the result and use the map operator instead, something like:
combineLatest(this._route.pathFromRoot.map(route => route.params)).pipe(
map(Object.assign)
);
Reference
Also, not sure about the intent of the code, since it seems as though it's calling Object.assign on a new array reference, not sure why that's necessary, but that's not entirely relevant.
I have an array of values [1,2,3] .
I want to emit each value with delay
I've managed to do it with the zip operator :
Rx.Observable.from([1,2,3])
.zip(Rx.Observable.timer(0, 1000), x => x)
.subscribe((e) => console.log(e));
Question:
Is there any more appropriate operator for such task ? Involving an inner observable seems ( to me) incorrect approach.
Should I unsubscribe the inner Observable manually ? Becuase basically no one tells it to stop.
jsbin
You can delay each emission itself and wait until the previous one completed. Like this for example:
Rx.Observable.from([1,2,3])
.concatMap(x => Observable.of(x).delay(1000)) // or Observable.timer(1000).mapTo(x)
.subscribe((e) => console.log(e));
If you want to use zip you don't need to unsubscribe the timer but you need to tell it to complete (for example with take() or takeUntil()).
As a functional programmer I want to keep my main code free from side effects and shift them to the edge of the application. ES2015 Iterators and the Iteration Protocols are a promising way to abstract specific collections. However, Iterators are also stateful. Can I still avoid side effects if I rely on immutable Iterables exclusively?
Iterators cause observable mutations
Iterators have one essential property: They decouple the consumer from the producer of the Iterable by serving as an intermediary. From the consumer point of view the data source is abstracted. It might be an Array, an Object or a Map. This is totally opaque to the consumer. Now that the control of the iteration process is moved from the producer to the Iterator, the latter can establish a pull mechanism, which can be lazily used by the consumer.
To manage its task an Iterator must keep track of the iteration state. Hence, it needs to be stateful. This is per se not harmful. However, it gets harmful as soon as state changes are observable:
const xs = [1,2,3,4,5];
const foo = itor => Array.from(itor);
const itor = xs.keys();
console.log(itor.next()); // 0
// share the iterator
console.log(foo(itor)); // [2,3,4,5] => observed mutation
console.log(itor.next()) // {value: undefined, done: true} => observed mutation
These effects occur even if you only work with immutable data types.
As a functional programmer you should avoid Iterators or at least use them with great care.
A pure iterator is dead simple. All we need is
the current value
a closure that advances the iterator
a way to signal that the iterator is exhausted
an appropriate data structure containing these properties
const ArrayIterator = xs => {
const aux = i => i in xs
? {value: xs[i], next: () => aux(i + 1), done: false}
: {done: true};
return aux(0);
};
const take = n => ix => {
const aux = ({value, next, done}, acc) =>
done ? acc
: acc.length === n ? acc
: aux(next(), acc.concat(value));
return aux(ix, []);
};
const ix = ArrayIterator([1,2,3,4,5]);
console.log(
take(3) (ix));
console.log(
ix.next().value,
ix.next().value,
ix.next().next().value)
No global state anywhere. You can implement it for any iterable data type. take is generic, that is it works with iterators of any data type.
Can anyone please explain me why native iterators are stateful? Why do the language designer hate functional programming?