Lazily delay each value of a infinite stream - rxjs

As the title suggests, I want to delay each value in an iterable by some amount of time while keeping the iterable lazily evaluated. Here's the closest I've got so far which works fine for finite iterables or those which don't throw an error
function* iter () {
let i = 0
while (true) yield i++
}
rxjs.zip(
rxjs.from(iter()),
rxjs.timer(500, 500),
x => x
).subscribe(console.log)

Another way might be:
const source$ = interval(0);
source$.pipe(
concatMap(x =>
of(x).pipe(delay(500))
)
).subscribe(console.log);

Related

RxJS withLatestFrom skips initial Subject value

In the following example, for whatever reason, initial value is ignored.
const frameRateSubject: BehaviorSubject<number> = new BehaviorSubject(24);
const loadedMetadata$: Observable<Event> = fromEvent(this.videoElement, 'loadedmetadata');
frameRateSubject.asObservable()
.pipe(
withLatestFrom(loadedMetadata$), // by commenting out this line, both 24 and 20 values are received
tap(([frameRate]: [number, Event]) => {
// initial value of 24 is never received, why is it?
console.log('frameRateSubject', frameRate)
})
)
.subscribe();
setTimeout(() => {
frameRateSubject.next(20)
}, 10000)
Any ideas why?
withLatestFrom combines the source observable (here frameRateSubject$) with other streams (loadedMetadata$) and emits values calculated from the latest values of each, only when the source emits.
But in your case loadedMetadata$ hasn't emitted when frameRateSubject$ emits 24. So the value is skipped.
CombineLatest is most likely the operator you are looking for here.

How to skip/consume/swallow a value after tapped into it?

I am trying to achieve something like below
from([1,2,3,4]).pipe(
Filter(v=> v % 2 === 0),
Tap( () => call server for even number only),
Swallow it, don't emit
).subscribe(()=>{log my odd numbers})
I know I can use filter( () => false) but was wondering if there is a better solution to it? Thanks
What do you mean? Swallow what? Based on what? Filter accepts/rejects values based on a predicate, if you want a better solution, what criteria would you prefer?
If you want to swallow everything, that's not functionally much different from terminating your Observable:
of([1,2,3,4]).pipe(
filter(v=> v % 2 === 0)
).subscribe(() => call server here);
Though I would expect an error as you can't use % on arrays. Though this should work:
from([1,2,3,4]).pipe(
filter(v=> v % 2 === 0)
).subscribe(() => call server here);
or
of([1,2,3,4]).pipe(
filter(v=> v.length % 2 === 0)
).subscribe(() => call server here);
Higher-order operators
If your server call is an observable-like object (promise, array, observable, ect), then you can use the higher-order operators like mergeMap, switchMap, concatMap, ect. In this case, you're not 'swallowing' a value so much as transforming (mapping) it into your call.
This also means whatever value your call server observable emits still gets passed forward. This really is a stream transformation rather than filtering/swallowing values.
from([1,2,3,4]).pipe(
filter(v=> v % 2 === 0),
concatMap(v => call server observable)
);
Update
Helper Function
/****
* Pipeable Operator:
* Takes arrays emitted by the source and spaces out their
* values by the given interval time in milliseconds
****/
function intervalArray<T>(intervalTime = 1000): OperatorFunction<T[], T> {
return pipe(
concatMap((v: T[]) =>
concat(
...v.map((value: T) =>
EMPTY.pipe(
delay(intervalTime),
startWith(value)
)
)
)
)
);
}
Mocking a hot observerable emitting values overtime:
const source = of([1,2,3,4,5]).pipe(
intervalArray(250),
shareReplay(1)
);
Solution
Here is how I might implement your question for such an observable. The simplest thing to do is to just subscribe to your observable twice:
source.pipe(
filter(v => v % 2 === 0),
concatMap(evenNumber => this.serviceCall(evenNumber))
).subscribe(/* serviceCallResult => Do Nothing */);
source.pipe(
filter(v => v % 2 !== 0)
).subscribe(oddNumber => console.log(
"I got an odd Number: ",
oddNumber
));

RxJS throttle same value but let new values through

"Here you have", someone says and you are given this input stream of values that you somewhat want to do distinctUntilChanged() upon...
Input: '1-1----11---2--1122----1---2---2-2-1-2---|'
Output: '1-----------2--1-2-----1---2-------1-2---|'
Nothing weird so far,
But now someone says "it's okey" if the same value comes again, "but only if it's not to soon!". I want at least '----' ticks between the same value. "Okey" you say and you add a throttle
const source = new Subject<number>();
// mysterious cave troll is randomly source.next(oneOrTwo)
const example = source.pipe(throttle(val => interval(4000)));
Input: '1-1----11---2--1122----1---2---2-2-1-2-----|'
Output: '1------1----2----2-----1-------2-----2-----|'
"That's not what I want! Look at all the value you missed", referring to that you throttle in regards to all values being streamed.
Input: '1-1----11---2--1122----1---2---2-2-1-2-----|'
Output: '1------1----2----2-----1-------2-----2-----|'
'-------------->1<--------->2<----->1<------|' <-- Missed values
"Here, let me show show you" the mysterious man says and gives you this
Wanted output
Input: '1-1----11---2--1112----1---2---2-2-1-2-----|'
Output: '1------1----2--1--2----1---2-----2-1-------|'
My answer to this is that it feels like a combined window wouldn't do.
From someone more experienced,
is this a hard problem to solve? (or have I missed an obvious solution)
First I came up with idea to somehow combine distinctUntilChanged() and throttleTimte(), however it was not possible for me to come up with solution and then I tried something else.
The operator I came up with is throttleDistinct() that works as you would like to: StackBlit Editor Link
It has 2 parameters which are:
duration: number which is in milliseconds and is similar to
duration in throttleTime(duration: number)
equals: (a: T, b: T) => boolean which is function to compare if previous item is equal to next item, which has default
implementation of (a, b) => a === b
import { of, fromEvent, interval, Observable } from 'rxjs';
import { map, scan, filter, } from 'rxjs/operators';
const source = fromEvent(document, 'keypress')
.pipe(map((x: any) => x.keyCode as number))
source
.pipe(
throttleDistinct(1000),
)
.subscribe((x) => console.log('__subscribe__', x));
export function throttleDistinct<T>(
duration: number,
equals: (a: T, b: T) => boolean = (a, b) => a === b
) {
return (source: Observable<T>) => {
return source
.pipe(
map((x) => {
const obj = { val: x, time: Date.now(), keep: true };
return obj;
}),
scan((acc, cur) => {
const diff = cur.time - acc.time;
const isSame = equals(acc.val, cur.val)
return diff > duration || (diff < duration && !isSame)
? { ...cur, keep: true }
: { ...acc, keep: false };
}),
filter((x) => x.keep),
map((x) => x.val),
)
}
}
Off the top of my head, you want to buffer by the time interval, then distinct within each buffer.
Effectively you want to restart / reboot the distinct run every n milliseconds.
source.pipe(
bufferTime(ms),
mergeMap(bufferArray => from(bufferArray).pipe(distinctUntilChanged()) )
)
This is my second attempt, it filters the stream by output (rather than taking distinctUntil) then throttles and merges the two streams.
Of course, we may not have a known set of values (1,2,...n).
If I can figure out that wrinkle, will add a further example.
const output = merge(
source.pipe( filter(x => x === 1), throttle(val => interval(ms))),
source.pipe( filter(x => x === 2), throttle(val => interval(ms)))
)
Here is my check (ms = 4000)
input 1-1----11---2--1112----1---2---2-2-1-2-----
expected 1------1----2--1--2----1---2-----2-1-------
filter(1) 1-1----11------111-----1-----------1-------
throttle(1) 1------1-------1-------1-----------1-------
filter(2) ------------2-----2--------2---2-2---2-----
throttle(2) ------------2-----2--------2-----2---------
merged 1------1----2--1--2----1---2-----2-1-------
expected 1------1----2--1--2----1---2-----2-1-------
Extending to n values
I think this will work where the set of values in the stream is not known in advance (or has a large range so extending the previous answer is impractical).
It should work as long as the source completes.
merge(
source.pipe(
distinct().pipe(
mapTo(distinctVal => source.pipe(
filter(val = val === distinctVal),
throttle(val => interval(ms))
)
)
)
)
I don't have a proof yet, will post that next.
Here is a tricky solution base on theory of operators, but I can't sure it really works, because I will need to mock a source emission first.
So throttle and distinct stream always have the latest value cached, zip make sure they always got emitted in pair, zip will always emit when any of the stream emit because it's shareReplay(1).
We always take the value emit from distinctStream, even when zip stream is trigger by throttle, because distinctStream always have the last cached value.
const throttleStream= source.pipe(throttle(val => interval(4000)),shareReplay(1))
const distinctStream= source.pipe(distinctUntilChanged(),shareReplay(1))
zip(throttleStream,distinctStream).pipe(
map((t,d)=>d)
)
I found a solution that works, does someone have any take on this?
source.pipe(
windowTime(4000),
concatMap(obs => obs.pipe(distinct()))
);
Examples from before, in a StackBlitz example
UPDATE: this does not actually work 100%. It only take the current window into consideration. So you can for example have
`[1-12][2---]` which would give `1--22---|`
where [----] would represent the time window. In other words, if a value is first emitted last in one window and emitted first in the next window, the same value will pass through right after each other.
Thanks #eric99 for making me realize this.

What is the best way to implement a poller with timeout as a reactive stream.

What is the best way to model a poller with a timeout, where a certain condition causes an early-exit as 'reactive streams'?
e.g.
If I had an observable which produced a decreasing sequence of positive integers every second
9,8,7,6,5,4,3,2,1,0
What is the best way to write a consumer which takes the latest single event after 5 seconds OR the '0' event if it produced earlier than the timeout.
This is my code as it stands at the moment: (Example in Java)
int intialValue = 10;
AtomicInteger counter = new AtomicInteger(intialValue);
Integer val = Observable.interval(1, TimeUnit.SECONDS)
.map(tick -> counter.decrementAndGet())
.takeUntil(it -> it == 0)
.takeUntil(Observable.timer(5, TimeUnit.SECONDS))
.lastElement()
.blockingGet();
System.out.println(val);
if initialValue = 10, I expect 6 to print. if initialValue = 2, i expect 0 to print before the 5 second timeout expires.
I'm interested if there is a better way to do this.
I don't think there is really a much better way than what you did. You have to have the following:
An interval to emit on (interval)
An aggregator to decrement and store the last value (scan)
A termination condition on the value (takeWhile)
A termination condition on time (takeUntil(timer(...)))
Get the last value on completion (last)
Each one is represented by an operator. You can't do much to get around that. I used a few different operators (scan for aggregation and takeWhile for termination on value) but it is the same number of operators.
const { interval, timer } = rxjs;
const { scan, takeWhile, takeUntil, last, tap } = rxjs.operators;
function poll(start) {
console.log('start', start);
interval(1000).pipe(
scan((x) => x - 1, start),
takeWhile((x) => x >= 0),
takeUntil(timer(5000)),
tap((x) => { console.log('tap', x); }),
last()
).subscribe(
(x) => { console.log('next', x); },
(e) => { console.log('error', e); },
() => { console.log('complete'); }
);
}
poll(10);
setTimeout(() => { poll(2); }, 6000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.1.0/rxjs.umd.min.js"></script>
I'm not clear on how you expect it to function on the boundaries. In your example you always decrement before emiting so if your initial value is 10 then you emit 9, 8, 7, 6 (4 values). If you wanted to start with 10 then . you could do scan(..., start + 1) but that would end you at 7 because the timer in the takeUntil(...) aligns with the source interval so that 6 would be excluded. If you want to emit 5 values then you could do takeUntil(timer(5001)). Also, if you don't want to wait a second to emit the first value then you could put startWith(start) right after the scan(...). Or you could do timer(0, 1000) with scan(..., start + 1) instead of the source interval.
Also note that the termination on value (takeWhile) will not terminate till the invalid value is produced (-1). So it will continue for a second after receiving the termination value (0). It seems that most of the termination operators work that way where if they terminate on some value then they wont let the others through.
You could do a take(5) instead of takeUntil(timer(5000)) because you know it fires on a matching interval if that works for your scenario. That would also get around the issue of excluding the last value because of the timers lining up.

'of' vs 'from' operator

Is the only difference between Observable.of and Observable.from the arguments format? Like the Function.prototype.call and Function.prototype.apply?
Observable.of(1,2,3).subscribe(() => {})
Observable.from([1,2,3]).subscribe(() => {})
It is important to note the difference between of and from when passing an array-like structure (including strings):
Observable.of([1, 2, 3]).subscribe(x => console.log(x));
would print the whole array at once.
On the other hand,
Observable.from([1, 2, 3]).subscribe(x => console.log(x));
prints the elements 1 by 1.
For strings the behaviour is the same, but at character level.
Not quite. When passing an array to Observable.from, the only difference between it and Observable.of is the way the arguments are passed.
However, Observable.from will accept an argument that is
a subscribable object, a Promise, an Observable-like, an Array, an iterable or an array-like object to be converted
There is no similar behaviour for Observable.of - which always accepts only values and performs no conversion.
One line Difference :
let fruits = ['orange','apple','banana']
from : Emit the items one by one of array. For example
from(fruits).subscribe(console.log) // 'orange','apple','banana'
of : Emit the whole array at once. For example
of(fruits).subscribe(console.log) // ['orange','apple','banana']
NOTE: of operator can behave as from operator with spread operator
of(...fruits).subscribe(console.log) // 'orange','apple','banana'
Another interesting fact is Observable.of([]) will be an empty array when you subscribe to it.
Where as when you subscribe to Observable.from([]) you wont get any value.
This is important when you do a consecutive operation with switchmap.
Ex:
In the below example, I am saving a job and then sites, and then comments as a stream.
.do((data) => {
this.jobService.save$.next(this.job.id);
})
.switchMap(() => this.jobService.addSites(this.job.id, this.sites)
.flatMap((data) => {
if (data.length > 0) {
// get observables for saving
return Observable.forkJoin(jobSiteObservables);
} else {
**return Observable.of([]);**
}
})).do((result) => {
// ..
})
.switchMap(() => this.saveComments())
....
if there's no site to save, ie; data.length = 0 in addSite section, the above code is returning Observable.of([]) and then goes to save comments. But if you replace it with Observable.from([]), the succeeding methods will not get called.
rxfiddle
of will emit all values at once
from will emit all values one by one
of with spread operator = from operator
from: Create observable from array, promise or iterable. Takes only one value. For arrays, iterables and strings, all contained values will be emitted as a sequence
const values = [1, 2, 3];
from(values); // 1 ... 2 ... 3
of: Create observable with variable amounts of values, emit values in sequence, but arrays as single value
const values = [1, 2, 3];
of(values, 'hi', 4, 5); // [1, 2, 3] ... 'hi' ... 4 ... 5
from returns notification in chunks i.e. one by one.
for eg: from("abcde") will return a => b => c => d => e
of returns complete notification.
for eg: of("abcde") will return abcde.
https://stackblitz.com/edit/typescript-sckwsw?file=index.ts&devtoolsheight=100
The from operator takes source of events. from(source)
let array = [1,2,3,4,5]
from(array); //where array is source of events, array[of events]
let promise = new Promise(function(resolve, reject) {
// executor (the producing code, "singer")
});
from(promise); //where promise is source of event, promise(of event)
let observable = Observable.create(function(observer) {
observer.next(1);
observer.next(2);
observer.next(3);
observer.next(4);
observer.next(5);
observer.complete();
});
from(observable); // where obsservable is source of events.
The of operator takes intividual events. of(event1, event2, event3)
of(1,2,3,4,5); // where 1,2,3,4,5 are individual events
I found it easier to remember the difference when the analogy with .call / .apply methods came into my mind.
You can think of it this way:
normally, all arguments, that are passed separately (separated by comma), are also emitted separately, in the order they were passed. of() just emits all arguments one by one as they are (like .call method passes arguments to the function it was called on)
from() is like .apply in a sense that it can take an array of values as an argument, and convert array elements into separate arguments, separated by comma.
So, if you have an array and want each element to be emitted separately, you can use from() or get the same behavior by using of() with spread operator, like of(...arr).
It's bit more complicated then that (from can also take observables) but with this analogy it will probably be easier to remember the main difference.
Yes it is true that of will result in an output in single go and from will happen one at a time. But there is more difference related to number of arguments and type of arguments.
You can pass any number of arguments to the Of. Each argument emitted separately and one after the other. It sends the Complete signal in the end.
However you can send only one argument to the from operator and that one argument should be a type of
an Array,
anything that behaves like an array
Promise
any iterable object
collections
any observable like object
For example you can send a raw object like
myObj={name:'Jack',marks:100}
to of operator to convert to Observable.
obj$:Observable<any> = of(myObj);
but you can not send this raw object myObj to from operator simply because it is not iterable or array like collection.
for more detail : visit here
from operator may accept one of
promises
iterable
arrays
observable
from emits each individual item from the observable , can also do conversions.
of operator takes in the raw value and emits the value from the observable.
import {from, Observable, of} from 'rxjs';
const ofObs = of([1,2,3]);
const fromObs = from([2,3,4]);
const basicObs = Observable.create(observer=>{
observer.next(100);
observer.next(200);
observer.next(300);
})
const promise = new Promise((resolve,reject)=>{
resolve(100);
})
const array = [1,2,3];
const iterbale = "Dhana";
// const myObs = from(ofObs);//possible and can emit individual item value everytime 1, then ,2 , then 3
// const myObs = from(fromObs);//possbile and can emit individual item value everytime 1, then ,2 , then 3
// const myObs = from(basicObs);//possbile and can emit individual item value everytime 100, then ,200 , then 300
const myObs = from(promise);//possible can emit value 100
// const myObs = array(promise);//possible and can emit individual item value everytime 1, then ,2 , then 3
// const myObs = iterable(promise);//possible and can emit individual item value everytime D then h then a then n then a
myObs.subscribe(d=>console.log(d))
import {from, of} from 'rxjs';
const basicOf1 = of([1,2,3,4,5,6]) // emits entire array of events
const basicfrom1 = from([1,2,3,4,5,6]) //emits each event at a time
const basicOf2 = of(1,2,3,4,5,6) // emits each event at a time
// const basicfrom2 = from(1,2,3,4,5,6) //throws error
//Uncaught TypeError: number is not observable
const basicOf3 = of(...[1,2,3,4,5,6]) // emits each event at a time
const basicfrom3 = from(...[1,2,3,4,5,6]) //throws error
//Uncaught TypeError: number is not observable
basicOf3.subscribe(d=>console.log(d))
Here is the link to codepen

Resources