In RXJS, is there a way to omit the res and err calls on .subscribe()? - rxjs

Given an observable where you subscribe:
Observable.subscribe(
(res) => {},
(err) => {},
() => {
// do some thing on complete
}
);
Is it a requirement to write (res) and (err)?
Reason being, I'm using Observable.forkJoin([Observable1, Observable2]).subscribe, and I only care about when they are done, and maybe if they yield an error.

To omit the next and error functions, pass undefined for those parameters:
Rx.Observable
.from([0, 1, 2])
.subscribe(
undefined,
undefined,
() => console.log("completed")
);

I would opt to use the .finally() operator instead of omitting the res and err callbacks. This to better convey what it is that you want to happen.
Rx.Observable.forkJoin([obs1, obs2])
.finally(() => console.log('done'))
.subscribe();
Note that it is not required to pass the callbacks to subscribe().

If we are talking of RxJS5 at least, you can also define the specific subscriber methods you want.
Observable.subscribe(
(res) => {},
(err) => {},
() => {
// do some thing on complete
}
);
becomes
Observable.subscribe(
complete: () => {
// do some thing on complete
}
);
As a side note, the other methods that can be explicitly defined are next and error.

Related

How can you execute and array of Observables API calls where each API calls waits for the previous?

What I have been playing with is to use combineLatest with concatAll() but they are still being called simultaneously. I could just loop and call each but I am always wondering if there is a better way within the RXJS workflow.
combineLatest(arrayOfApiObservables).pipe(concatAll()).subscribe();
The problem here is that you use the combineLatest operator, which will emit value only after all observables had emitted (e.g. it is calling everything simultaneously).
After that the concatAll can't affect the arrayOfApiObservables because they have alredy been called.
The right aproach is to create a higher-order observable (observable that emits observables), which can be achived with the help of the operator from and after that you can concatAll them to achive the desired result.
concatAll definition as seen in the docs: Converts a higher-order Observable into a first-order Observable by concatenating the inner Observables in order..
let {
interval,
from
} = rxjs
let {
take,
concatAll,
mapTo
} = rxjs.operators
let ref = document.querySelector('#container')
const obs1$ = interval(1000).pipe(take(1), mapTo('obs1'));
const obs2$ = interval(500).pipe(take(1), mapTo('obs2'));
const obs3$ = interval(2000).pipe(take(1), mapTo('obs3'));
let allObservables$ = from([obs1$, obs2$, obs3$])
allObservables$.pipe(
concatAll()
).subscribe((x) => {
console.log(x)
container.innerHTML += `<div>${x}</div>`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"></script>
<div id="container"></div>
If you want them to be executed in a sequential manner, you could use concatMap() inside pipe().
Something like this:
of(1)
.pipe(
concatMap(result => {
console.log(result);
return of(2);
}),
concatMap(result => {
console.log(result);
return of(3);
}),
concatMap(result => {
console.log(result);
return of(4);
}),
concatMap(result => {
console.log(result);
return of(5);
}),
concatMap(result => {
console.log(result);
return of(6);
})
)
.subscribe(res => {
console.log("finish");
});
But, if you want to execute them all at once and then await for them until they are all completed, then just use forkJoin().

How do I iterate over functions that return rxjs observables

I want to iterate over a series of asynchronous functions and end the iterating when a false is returned.
I'm new to rxjs and can't get the use-case below to work. I feel like I'm not understanding something fundamental. Can someone please point it out to me?
function validateA(): Observable<any> {
// do stuff.
return of({ id: "A", result: true }); // hardcoding result for now
}
function validateB(): Observable<any> {
// do stuff
return of({ id: "B", result: true }); // hardcoding result for now
}
function validateC(): Observable<any> {
// do stuff
return of({ id: "C", result: false });// hardcoding result for now
}
from([validateA, validateB, validateC])
.pipe(
map(data => data()),
takeWhile(data => !!data.result)
)
.subscribe(data => console.log(`${data.id} passed!`));
https://stackblitz.com/edit/typescript-ub9c5r?file=index.ts&devtoolsheight=100
I would say that the core of your logic is right. What is missing is some rxJs pecularity.
The solutions could be something like this. Explanation of the nuances are in the comments.
// start from an array of functions and turn it into a stream using RxJs from function
from([validateA, validateB, validateC])
.pipe(
// now execute each function sequentially, one after the other, via concatMap
// operator. This operator calls each function and each function returns an Observable
// concatMap ensures that the functions are called sequentially and also that the returned Observable (because each function returns an Observable)
// is "flattened" in the result stream. In other words, you execute each function one at the time
// and return the value emitted by the Observable returned by that function
// until that Observable completes. Considering that you use the "of" function to
// create the Observable which is returned by each function, such Observable emits just one value and then completes.
concatMap(func => func()),
// now you have a stream of values notified by the Observables returned by the functions
// and you terminate as soon as a flase is received
takeWhile(data => !!data.result)
)
.subscribe(data => console.log(`${data.id} passed!`));
The following seems to do the trick and calls functions lazily:
https://stackblitz.com/edit/typescript-9ystxv?file=index.ts
import { from, Observable, of } from "rxjs";
import { concatAll, find, map } from "rxjs/operators";
function validateA() {
console.log('validateA');
return of({ id: "A", result: false });
}
function validateB() {
console.log('validateB');
return of({ id: "B", result: true });
}
function validateC() {
console.log('validateC');
return of({ id: "C", result: false });
}
from([validateA, validateB, validateC])
.pipe(
map(validate => validate()),
concatAll(),
find(data => data.result)
)
.subscribe(data => console.log(`${data.id} passed!`));

Can a series of promises and then handlers be written in waitOneCycle.then(waitOneCycle().then(...)) but without deep nesting?

If we already have
const waitOneCycle = () => {
return new Promise(resolve => setTimeout(resolve));
};
Then our code (this is currently used in Jest and SinonJS although it doesn't have to be:)
waitOneCycle()
.then(() => {
// do something
});
and it reads really elegantly: the waitOneCycle and then do something.
However, if we do a series of them, we have to:
waitOneCycle()
.then(() => {
// do something
return waitOneCycle();
})
.then(() => {
// do something
return waitOneCycle();
})
.then(() => {
// do something
return waitOneCycle();
});
and it reads in a strange way, because why would we return something to act as "waitOneCycle"? This is the way it works, but the code just read in a strange way.
We could do something like:
waitOneCycle()
.then(() => {
// not return anything here
})
.then(() => {
waitOneCycle()
.then(() => {
});
})
but this would go back to the nesting hell issue in the past. Is there a way to refactor it so it reads coherently but at the same time all actions are serialized?
P.S. in the case of Ruby, if the promises are chained this way, it actually can read out quite well, as we don't need to use return for the last value evaluated. So it'd read like waitOneCycle, then, waitOneCycle, then... and it appears quite elegantly.
You don't need to nest (and it would only work if you were to return the inner promise anyway), you can also chain it:
waitOneCycle()
.then(() => {
// do something
})
.then(waitOneCycle)
.then(() => {
// do something
})
.then(waitOneCycle)
.then(() => {
// do something
})
.then(waitOneCycle);
This might work even better if you make waitOneCycle pass through the value:
const waitOneCycle = (v) => {
return new Promise(resolve => setTimeout(resolve, v));
};
Of course, if you want promise code that reads coherently and has no nesting (not even that of then callbacks), you should just go for async/await syntax.

Retry operator working with observable but not with subject

I try to use rxjs rety operator, its work fine with observable:
const obs$: Observable<number> = new Observable((observer) => {
observer.next(1);
observer.complete();
});
obs$
.pipe(
mergeMap(async () => [
await dataService.getAllSomthing(),
await dataService.getAllomthingElse(),
await dataService.getAllSomthingElseElse(),
]),
map(([somthing, somthingElse, somthingElseElse]) => {
dispatch(
enableToasterAction({
text: "good",
type: ToasterType.Success,
})
);
}),
retry(2),
catchError((err) => {
return of(null);
})
)
.subscribe((val: any) => {});
But its not work when I using Subject:
const sub=new Subject();
sub
.pipe(
switchMap(async ({ somthinng}: any) => {
return [await dataService.getSomthing(somthinng)];
}),
map(([somthinngRes]) => {
dispatch(
enableToasterAction({
text: "good",
type: ToasterType.Success,
})
);
}),
retry(2),
catchError((err) => {
return of(null);
})
)
.subscribe((val: any) => {});
sub.next({});
Someone can help me to understand what the difference between them, why its work with observable, but not with subject ?
You can retry an cold observable but not hot observable (subject)
if you want to retry a action trigger by hot observable, you can however move the retry() operator to inner observable. For example
fromEvent(document,'click').pipe(
switchMap(evt=>from(fetch('someurl')).pipe(retry(2))
)
That way the http call triggered by click will retry 2 times when it fails
Subject has an internal state and once it receives complete or error notification it marks itself as isStopped and will never ever emit anything.
So retry() tries to resubscribe but the source Subject will just return Subscription.EMPTY and won't make a real subscribtion.

RXJS: How to throttle Observable from Promise

I am using RxJS 5.5.10.
I try to throttle an observable to fire every 5 seconds.
This Observable is based on a Promise.
Rx.Observable.fromPromise(mongo.AllWishes)
.flatMap(array => Rx.Observable.from(array))
.pluck('url')
.filter(s => s !== undefined)
.subscribe(m => console.log(m))
I understand that I can use the throttle operator to emit values only after a given time
Rx.Observable.interval(1000)
.throttle(val => Rx.Observable.interval(5000)
.subscribe(m => console.log('ping'))
But when I try something like
Rx.Observable.fromPromise(mongo.AllWishes)
.throttle(val => Rx.Observable.interval(5000))
.flatMap(array => Rx.Observable.from(array))
.pluck('url')
.filter(s => s !== undefined)
.subscribe(m => console.log(m))
I get an error
rxjs/observable/PromiseObservable.js:76
root_1.root.setTimeout(function () { throw err; });
^
TypeError: this.durationSelector is not a function
What am I missing ?
Thank your for your help
I'm not entirely clear on your expectations. It looks like you are getting an array from a promise and are then wanting to emit each value sequentially with 5 seconds in-between each item.
If so, I think that this should do what you want. As far as your error it is hard to tell without being able to run your code. I'm assuming it has something to do with your promise since I can replace mongo.AllWishes with my own promise and it doesn't error.
const data = [
{ url: 'https://something.com/1' },
{ url: 'https://something.com/2' },
{ url: 'https://something.com/3' },
{ url: 'https://something.com/4' },
{ url: 'https://something.com/5' }
];
const myPromise = new Promise((resolve) => {
setTimeout(() => { resolve(data); }, 1000);
});
Rx.Observable.fromPromise(myPromise)
.flatMap(x => {
return Rx.Observable.timer(0, 1000)
.takeWhile(i => i < x.length)
.map(i => x[i]);
})
.pluck('url')
.subscribe((url) => { console.log(url); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.10/Rx.min.js"></script>

Resources