How to rewrite merge on new Rxjs? - rxjs

I have old Rxjs merge:
Observable.merge(this.timers).subscribe(x => {
let subscription = x.subscribe((value: ITimer) => {});
});
How to rewrite it on new Rxjs notation?
I have tried import this:
import { merge } from "rxjs/operators";

As you can see: merge is still part of rxjs, it doesn't live as an operator... It is just a factory from which you can create a new observable out of n...
const { merge, from } = rxjs;
const a$ = from([1, 2, 3, 4]);
const b$ = from(['hello', 'world', 'dude']);
merge(a$, b$).subscribe(console.log);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.js" integrity="sha256-Nihli32xEO2dsnrW29M+krVxoeDblkRBTkk5ZLQJ6O8=" crossorigin="anonymous"></script>

Related

RxJS - Same without Subscription / BehaviorSubject?

I am looking for an alternate way to do :
import { interval, BehaviorSubject } from 'rxjs';
const source$ = interval(1000);
const store$ = new BehaviorSubject<number[]>([]);
source$.subscribe((point) => store$.next([...store$.value].concat(point)));
// result
store$.subscribe(console.log);
I indeed would like not to use subscription at all, and so I guess no BehaviorSubject.
Thanks for any hint.
You can use a scan operator.
const source$ = interval(1000);
const store$ = source$.pipe( // or simply interval(1000).pipe(
scan((result, value) => [...result, value], []),
);
// result
store$.subscribe(console.log); // [0], [0, 1], ...

RXJS Emit array item on event trigger

Using RxJS Id like to emit each array item on an event/click.
I have below which works fine, but is there a cleaner way?
const testEventClick = new Subject();
const array = [1, 2, 3, 4, 5];
from(array)
.pipe(
concatMap(val => {
return new Observable(sub => {
testEventClick.subscribe(x => {
sub.next(val);
sub.complete();
});
});
})
)
.subscribe(console.log);
testEventClick.next();
testEventClick.next();
I would do the other way around and observe the subject.
Example: https://stackblitz.com/edit/rxjs-l4ttzh

RxJS Add an observable after subscribe

Is there a way to add one more element after an observable has been subscribed?
observable = timer(1000,2000).take(5)
observable.subscribe()
//Now I want to add one more element to observable....
Maybe a subject is what you are looking for, a subject is both an observable and an observer.
const { Subject, timer, fromEvent } = rxjs;
const { take } = rxjs.operators;
let subject$ = new Subject();
subject$.subscribe(val => { console.log(val); });
timer(1000,2000).pipe(take(5)).subscribe(val => { subject$.next(val); });
fromEvent(document.getElementsByTagName('button')[0], 'click').subscribe(() => {
subject$.next('clicked');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>
<button>Click</button>
You can use a combination of merge and Subjects
const { Subject, timer, merge } = rxjs;
const { take } = rxjs.operators;
let source_1 = timer(1000,2000).pipe(take(5));
let source_2 = new Subject();
let final_source = merge(
source_1,
source_2
);
final_source.subscribe(e => console.log(e));
source_2.next('jabadabadoo!');
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>

Is BehaviorSubject the same as shareReplay(1)?

In these two examples, does subject$ behave in exactly the same way?
import { BehaviorSubject, Observable } from 'rxjs';
let source: Observable<number>;
let subject$: Observable<number>;
subject$ = new BehaviorSubject(0);
source.subscribe(x => {
subject$.next(x);
});
import { Observable } from 'rxjs';
import { shareReplay, startWith } from 'rxjs/operators';
let source: Observable<number>;
let subject$: Observable<number>;
subject$ = source.pipe(
startWith(0),
shareReplay(1)
);
The basic difference is that the 0 value may be re-emitted to the upstream when all subscribers unsubscribe and similar subtleties when no subscribers are active.
e.g.:
import { BehaviorSubject, Subject } from 'rxjs';
const source = new Subject<number>();
const subject$ = new BehaviorSubject(0);
source.subscribe((x) => {
subject$.next(x);
});
source.next(1);
source.next(2);
let subscription = subject$.subscribe(console.log);
source.next(3);
source.next(4);
subscription.unsubscribe();
source.next(5);
source.next(6);
subscription = subject$.subscribe(console.log);
source.next(7);
source.next(8);
// Prints: 2 3 4 6 7 8
import { Subject } from 'rxjs';
import { shareReplay, startWith } from 'rxjs/operators';
const source = new Subject<number>();
const subject$ = source.pipe(startWith(0), shareReplay(1));
source.next(1);
source.next(2);
let subscription = subject$.subscribe(console.log);
source.next(3);
source.next(4);
subscription.unsubscribe();
source.next(5);
source.next(6);
subscription = subject$.subscribe(console.log);
source.next(7);
source.next(8);
// Prints 0 3 4 6 7 8
They replay behavior is the same but number of emission replay can be configured with shareReplay but not with BehaviorSubject. The main difference is shareReplay is an operator which can be add into pipe and convert any source stream to replay value and it doesn't replay any value until first emission happen.
BehaviorSubject is a class which only replay one value and need to be instantiated with a default value and thus when subscribe it always return one cached value.

RxJS emit array items over time?

I'm trying to emit simple array values one after another with 500ms in between:
var a = Rx.Observable.from([1,2,3]);
a.interval(500).subscribe(function(b) { console.log(b); });
However, this throws an exception:
Uncaught TypeError: a.interval is not a function.
Three ways to do it, with RxJS version 6 :
1. Using concatMap
import { from, of, pipe } from 'rxjs';
import { concatMap, delay } from 'rxjs/operators';
const array = [1, 2, 3, 4, 5];
from(array)
.pipe(
concatMap(val => of(val).pipe(delay(1000))),
)
.subscribe(console.log);
2. Using zip and interval
import { from, pipe, interval } from 'rxjs';
import { delay, zip} from 'rxjs/operators';
const array = [1, 2, 3, 4, 5];
from(array)
.pipe(
zip(interval(1000), (a, b) => a),
)
.subscribe(console.log);
3. Using interval as source
import { interval, pipe } from 'rxjs';
import { map, take } from 'rxjs/operators';
const array = [1, 2, 3, 4, 5];
interval(1000)
.pipe(
take(array.length),
map(i => array[i])
)
.subscribe(console.log);
As already pointed out by xgrommx, interval is not an instance member of an observable but rather a static member of Rx.Observable.
Rx.Observable.fromArray([1,2,3]).zip(
Rx.Observable.interval(500), function(a, b) { return a; })
.subscribe(
function(x) { document.write(x + '<br \>'); },
null,
function() { document.write("complete"); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.2/rx.all.min.js"></script>
This is how I would do it:
var fruits = ['apple', 'orange', 'banana', 'apple'];
var observable = Rx.Observable.interval(1000).take(fruits.length).map(t => fruits[t]);
observable.subscribe(t => {
console.log(t);
document.body.appendChild(document.createTextNode(t + ', '));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.2/rx.all.min.js"></script>
var arrayList = [1,2,3,4,5];
var source = Rx.Observable
.interval(500/* ms */)
.timeInterval()
.take(arrayList.length);
source.subscribe(function(idx){
console.log(arrayList[idx]);
//or document.write or whatever needed
});
Pretty late but a simpler solution would be :
const arr = ["Hi,", "how", "may", "I", "help", "you?"];
Rx.Observable.interval(500)
.takeWhile(_ => _ < arr.length)
.map(_ => arr[_])
.subscribe(_ => console.log(_))
I find Weichhold technique to be the best but that it would gain in clarity of intent by extracting the zipped value outside of the zip:
// assume some input stream of values:
var inputs = Obs.of(1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8);
// emit each value from stream at a given interval:
var events = Obs.zip(inputs, Obs.interval(1000))
.map(val => val[0])
.forEach(console.log);
If you want to release samples over time, you can do something like this
const observable = interval(100).pipe(
scan((acc, value) => [value, ...acc], []),
sampleTime(10000),
map((acc) => acc[0])
);
I had a little different requirement, my array kept updating over time too. So basically I had to implement a queue which I can dequeue at a regular interval, but I didn't want to use an Interval.
If somebody has a need for something like this then probably this solution can help:
I have a function createQueue() that takes the array as an input and returns an Observable which we subscribe for listening to events from the Array at a regular interval.
The function also modifies the 'push()' method of the passes array so that whenever any item is pushed in the array, the Observable would emit.
createQueue(queue: string[]) {
return Observable.create((obs: Observer<void>) => {
const arrayPush = queue.push;
queue.push = (data: string) => {
const returnVal = arrayPush.call(queue, data);
obs.next();
return returnVal;
}
}).pipe(switchMap(() => {
return from([...queue])
.pipe(
concatMap(val => of(val)
.pipe(delay(1000)))
);
}), tap(_ => queue.shift()))
}
Lets say that the array is: taskQueue = [];
So, we need to pass it to the above function and subscribe to it.
createQueue(taskQueue).subscribe((data) => {
console.log('Data from queue => ', data);
});
Now, every time we do taskQueue.push('<something here>'), the subscription will trigger after a delay of "1000ms".
Please note: we should not be assigning a new array to the taskQueue after createQueue() has been called, or else we will loose the modified push().
Here is a dummy example for the above implementation: Test Example
Rx.Observable instance doesn't have interval method http://xgrommx.github.io/rx-book/content/core_objects/observable/observable_instance_methods/index.html. You can use like this.
Rx.Observable.interval(500)
.map(function(v) { return [1,2,3];})
.subscribe(console.log.bind(console));

Resources