How can I emit values with delay in RXJS? - javascript

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()).

Related

recursive rxjs switchmap ish

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.

How to get rid of multiple nested switchMap with early returns

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)
);

DebounceTime Rxjs when same parameters being passed in

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
}),
)

RxJS: invoke two async calls, return the first that passes a condition, or a special value of none of them does

Using RxJS, I want to invoke two different asynchronous operations, which may or may not return null. They may also take indefinite time to complete.
I want to achieve the following:
If operation A returns value 1, then immediately return value 1
If operation B returns value 2, then immediately return value 2
If both operation A and B return null, then return null.
I suppose I can achieve the first two simply as follows:
const bothOperations = merge(callA(), callB());
const firstSuccess = bothOperations.first(res => res !== null);
return firstSuccess;
But how do I get the third possibility into this?
I think you could do it like this (I'm assuming you're using RxJS 5.5):
merge(callA(), callB())
.pipe(
filter(v !== null),
concat(of(null)),
take(1),
)
The take(1) operator makes sure only one value is passed and concat(of(null)) appends null after both source Observables completed.
Just note that if both source Observables run indefinitely than this will never complete. You could add timeout() operator however.

Rx: Auto-refreshing list(array)

I want to implement observable of array that auto-refresh itself. I do it like this:
const list$ = Observable.create(function(observer) {
getList(list => observer.next(threads);
}));
const liveList$ = Observable.interval(2000).switchMapTo(list$);
When I do subscribe to the liveList$ stream I get values only after n ms. as expected.
The question is how can I get values after first call of getList immediately and each next call with interval?
P.S. I've tried to $list.switchMapTo($liveList).subscribe(console.log) but nothing is changed in behaviour of the chain
Use the timer operator instead. It can be passed an initial delay, as well as a period:
Observable.timer(0, 2000).switchMapTo(list$);

Categories