Notify from inner flatMap - rxjs

Here a quite complex sample:
Main:
this.runInstructionAndGetResult().subscribe({
next: val => console.log(`NEXT VALUE: ${val}`),
error: val => console.log(`ERROR VALUE: ${val}`),
complete: val => console.log(`COMPLETE`)
});
Observables:
public runInstructionAndGetResult(): Observable<string> {
return this.runAnInstruction()
.flatMap((data) => {
console.info("flatMap of runAnInstruction:", data);
return this.getInstructionExecutionStatusInPolling()
.filter(data => data != "Polling")
.take(1)
.flatMap((data) => {
console.info("flatMap of getInstructionExecutionStatusInPolling:", data);
return this.getInstructionResult();
}).map((data) => {
console.info("Map of getInstructionResult:", data);
return data;
});
});
}
public runAnInstruction(): Observable<string> {
return Observable.of("StartRun");
}
public getInstructionResult(): Observable<string> {
return Observable.of("FinalResult");
}
public getInstructionExecutionStatusInPolling(): Observable<string> {
return Observable.interval(1000)
.concatMap(data => {
return this.getInstructionExecutionStatus();
});
}
public getInstructionExecutionStatus(): Observable<string> {
return Observable.of("Polling", "Terminate");
}
Here plunk:
https://plnkr.co/edit/c1cahMtVARQnLgnHWlEe?p=preview
Main problem is that i just would like to be notify about "evolution" of inner stream outside.
Right now we have "next" event on main only when all inner flatMap are completed.
How to get notify? How can i emit explicit values to main stream for example during polling?
Thanks.

I found a solution to share.
Here plunker updated:
https://plnkr.co/edit/c1cahMtVARQnLgnHWlEe?p=preview
Basically i create a simple observable using : https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/create.md
then i call programmatically next method and complete finally:
public runInstructionAndGetResult(): Observable<string> {
return Observable.create((ops)=> {
ops.next(1);
this.runAnInstruction()
.concatMap((data) => {
ops.next(2);
console.info("flatMap of runAnInstruction:", data);
return this.getInstructionExecutionStatusInPolling()
.filter(data => data != "Polling")
.take(1)
.concatMap((data) => {
ops.next(3);
console.info("flatMap of getInstructionExecutionStatusInPolling:", data);
return this.getInstructionResult();
}).map((data) => {
console.info("Map of getInstructionResult:", data);
ops.next(4);
ops.complete();
return data;
});
}).subscribe();
});
}

Related

returning rxjs function of type Observable<void>

I'm trying to convert the below observable code from using Observable.create to using pipeable operators.
public getUnrecoveredGearsAfterDep(): Observable<void> {
return Observable.create((observer) => {
this.vesselDetailService.getVessel().subscribe(
(vessel: Vessel) => {
console.log(vessel.cfr);
this.getAndUpdateUnrecoveredGears(vessel.cfr).subscribe(
() => {
observer.next(null);
observer.complete();
},
(error) => {
observer.error(error);
}
);
},
(error) => {
observer.error(error);
}
);
});
}
I've tried the below method but I'm getting an conversion error
cant convert type Observable.unknown to Observable.void
any help would be appreciated.
public altGetUnrecoveredGearsAfterDep(): Observable<void> {
return this.vesselDetailService.getVessel().pipe(
tap( (vessel: Vessel) => console.log(vessel.cfr)),
switchMap((vessel: Vessel) => this.getAndUpdateUnrecoveredGears(vessel.cfr)),
map((gears: GearSet[]) => of())
);
}
Your problem is that you are mapping your output to of() which is an observable. This gives your function a return type of Observable<Observable<never>>.
Your map should simply return undefined or null, you don't need of:
public altGetUnrecoveredGearsAfterDep(): Observable<void> {
return this.vesselDetailService.getVessel().pipe(
tap( (vessel: Vessel) => console.log(vessel.cfr)),
switchMap((vessel: Vessel) => this.getAndUpdateUnrecoveredGears(vessel.cfr)),
map((gears: GearSet[]) => undefined)
);
}
Also, you could simplify by using mapTo and removing the type annotations:
public altGetUnrecoveredGearsAfterDep(): Observable<void> {
return this.vesselDetailService.getVessel().pipe(
tap(vessel => console.log(vessel.cfr)),
switchMap(vessel => this.getAndUpdateUnrecoveredGears(vessel.cfr)),
mapTo(undefined)
);
}
At the end you are returning of(). The type of that value is Observable<unknown>, and it does not match the type in your return function Observable<void>.
To solve this you can simply return of(null).
You could return it like that:
import { of } from 'rxjs';
public altGetUnrecoveredGearsAfterDep(): Observable<void> {
//...
of(unit());
//...
}
function unit() {
return void 0;
}
It worked for me in unit testing very well.

Why doesn't EMPTY complete the observable?

In the code below, I am conditionally switching to another observable. If the condition is met it works fine. If the condition is not met and I return EMPTY from switchMap, the code in the subscribe block is not executed.
If I change return EMPTY to return of(x) it works.
this.claimStoreService.setProducts(this.claim.products)
.pipe(switchMap(x => {
if (this.incomeEligibility) {
return this.claimStoreService.saveIncomeEligibility();
} else {
return EMPTY;
}
}))
.subscribe(() => {
this.isSaving = false;
this.goIntoDisplayMode();
}, () => {
this.isSaving = false;
});
Try to use the third callback:
this.claimStoreService.setProducts(this.claim.products)
.pipe(switchMap(x => {
if (this.incomeEligibility) {
return this.claimStoreService.saveIncomeEligibility();
} else {
return EMPTY;
}
}))
.subscribe(
() => this.goIntoDisplayMode(),
console.err,
() => this.isSaving = false,
);
It is probably more clear if you pass to the subscribe function an Observer. Your code would become
this.claimStoreService.setProducts(this.claim.products)
.pipe(switchMap(x => {
if (this.incomeEligibility) {
return this.claimStoreService.saveIncomeEligibility();
} else {
return EMPTY;
}
}))
.subscribe({
next: () => this.goIntoDisplayMode(),
error: err => console.error(err),
complete: () => this.isSaving = false,
});

RxJs How to complete inner observable

I have function like this:
this.eventTaskWorking$ = completeStage
.pipe(
map(result => {
switch (result) {
case Statuses.LAST_TASK: {
console.info('returning finish event observable');
throw { err: 0 };
}
default: {
return EMPTY;
}
}
}),
catchError(() => completeEvent)
)
.subscribe();
When i throw an exception, "completeEvent" is completed, but if i try to use switchMap, mergeMap etc...it's not working:
this.eventTaskWorking$ = completeStage
.pipe(
map(result => {
switch (result) {
case Statuses.LAST_TASK: {
return completeEvent;
}
default: {
return EMPTY;
}
}
}),
switchMap(t => t),
)
.subscribe();
What's wrong?
UPD:
const completeEvent = this.FinishEvent(eventRef, uid);
private FinishEvent(eventRef: Observable<IEvent>, taskUid: string): Observable<any> {
return eventRef.pipe(
switchMap(t => this.UpdateTaskStatus(taskUid, 3)));
}
ok, seems FinishEvent didn't return observable, my fault

Call multiple ajax and wait for result Angular2

I have problem with my Angular. I have this functions:
private callUserInfo(): any {
this.isLoading = true;
return this._ajaxService.getService('/system/ping')
.map(
result => {
this.userId =
result.participant.substring(result.participant.indexOf('#'));
this.isLoading = false;
}
)
.catch(error => {
return Observable.throw(error);
});
}
public loadUserData(userName: string): any {
this.isLoading = true;
return this._ajaxService.getService('/User/' + userName)
.map(
result => {
const data = result[0];
this.user = new User(
data.id,
data.contacts[0].email,
data.name,
data.surname,
data.address.street,
data.address.city,
data.address.state,
data.address.country,
data.address.postCode,
data.address.timeZone);
this.isLoading = false;
})
.catch(error => {
return Observable.throw(error);
});
}
public getUser(): any {
if (this.user == null) {
this.callUserInfo().subscribe(() => {
this.loadUserData(this.userId).subscribe(() => {
return this.user;
});
});
} else {
return this.user;
}
}
In my component I call this service functions like this (auth service is service with functions defined up):
constructor(private _auth: AuthService) {
this.user = _auth.getUser();
}
But it stills return null (because Ajax calls are not finished?) Can someone explain me, how to call this two calls (first is system/ping service and based on return (userId) I need to call second ajax call (/user/id). After this two calls I have defined user in my service and I can return it to other components. Can someone expllain me, what am i doing wrong, or how I can do it better? I´m using newest version of angular.
P.S. Get service is from my wrapper service:
getService(url: string): Observable<any> {
return this.http
.get(this.base + url, this.options)
.map(this.extractData)
.catch(this.handleError);
}
You are not returning anything in case this.user==null
Change your function as following:
userObservabel=new BehaviourSubject(null);
public getUser(): any {
if (this.user == null) {
this.callUserInfo().subscribe(() => {
this.loadUserData(this.userId).subscribe(() => {
this.userObservabel.next(this.user);
});
});
return this.userObservabel.asObservable();
} else {
return this.userObservabel.asObservable();
}
}
and then you need to subscribe it
constructor(private _auth: AuthService) {
_auth.getUser().subscribe(user => this.user = user);
}
You need to call the second service in the subscribe or in the map method i.e. the Observable has returned a promise and that is resolved. Once that is resolved u should call your chained service.
A sample snipped from my POC might help you
this._accountListService.getAccountsFromBE().subscribe(
response => {
this.response = response;
this._accountListService.getAccountSorting().subscribe(
response => {
this.acctSort = response;
if (response.prodCode) {
this._accountListService.getAccountOrder().subscribe(
response => {
this.acctOrder = response;
this.response = this.setAccountOrder(this.response);
this.response.sort(this.myComparator);
this.acctFlag = true;
if (this.prodDesc) {
this.loader = false;
this.accountDetl = this.response[0];
this.accountDetl.entCdeDesc = this.prodDesc[this.accountDetl.entProdCatCde];
}
},
err => console.log(err)
);
}
},
err => console.log(err)
);
},
err => console.log(err)
);

Angular 2 Observable get into an Infinite loop

I'm very new to Angular2!. We have a search method which create an async observable and executing it. When provide a valid input, it works good. But an invalid input is causing an Infinite loop. Please let me know if you see the issue.
Please see the code below.
const ngPromiseToObservable = <T>(p: ng.IHttpPromise<ng.IHttpPromiseCallbackArg<T>>): Observable<ng.IHttpPromiseCallbackArg<T>> => {
const o = new Subject();
p.catch((e) => o.error(e));
p.then((v) => o.next(v));
return o;
};
export class HttpObservable
{
public static $inject = [
"$rootScope",
"$http",
"HttpErrors",
];
constructor(
private $rootScope: ng.IScope,
private $http: ng.IHttpService,
private httpErrors: Subject<Object>
) {}
handleHttpResponseErrors<T>(o: Observable<T>): Observable<T>
{
return o.do(
_.noop,
(e: HttpResponseError) => this.httpErrors.next(e),
_.noop
);
}
applyAsyncOnAction<T>(o: Observable<T>): Observable<T>
{
let applyAsync: () => void = () => {
return this.$rootScope.$applyAsync();
};
return o.do(applyAsync, applyAsync, applyAsync);
}
// tslint:disable-next-line:no-any
post<T>(url: string, data: any, config?: Object): Observable<T>
{
return this.handleHttpResponseErrors(this.applyAsyncOnAction(ngPromiseToObservable(this.$http.post(url, data, config)).map((x) => x.data)));
}
// tslint:disable-next-line:no-any
put<T>(url: string, data: any, config?: Object): Observable<T>
{
return this.handleHttpResponseErrors(this.applyAsyncOnAction(ngPromiseToObservable(this.$http.put(url, data, config)).map((x) => x.data)));
}
get<T>(path: string): Observable<T>
{
return this.handleHttpResponseErrors(this.applyAsyncOnAction(ngPromiseToObservable(this.$http.get(path)).map((x) => x.data)));
}

Resources