i want to know how to 'wait' without using async/await to finish observable and then return that value
// import {Observable, of} from 'rxjs';
getFirstQueue() {
if (this.insideVar) {
return of(this.insideVar);
}
this.http.get(this.createUrl(`some_resource`)).subscribe((someVar: any) => {
this.insideVar = someVar;
});
return this.insideVar;
}
You can't return from a subscription. You have to return an Observable. A function higher up the stack will then subscribe to this observable.
You can use tap to perform the same side-effect you are currently doing in the subscribe body before returning.
getFirstQueue(): Observable<any> {
if (this.activeQueue) {
return of(this.activeQueue);
}
return this.http.get(this.createUrl(`some_resource`)).pipe(
tap((someVar: any) => {
this.insideVar = someVar;
})
);
}
It is not possible to go from asynchronous callback to synchronous context. For that you need to use async/await, but if you take a look at the decompiled code the async/await is just a syntactic sugar on top of Promises, so it only looks like it came from async to sync
Related
I am not so into RxJS and I am finding some problems to understand this piece of code retrieved into an Angular project on which I am working.
First of all into a component TypeScript code I have this method:
async post(): Promise<void> {
this.submitted.next(true);
try {
await this.setAddress();
this.activeModal.close();
} catch (e) {
console.error('Storage upload error', e);
this.submitted.next(false);
}
}
As you can see this method have async prefix because into the try block it contains these 2 rows:
await this.setAddress();
this.activeModal.close();
from what I have understand (please correct me if I am doing wrong assertion) basically the await in front of this.setAddress() it means: await that this method call end, when it is completed executed the following operation (that in this case close a modal window).
From what I have understand it replave the then() method handling a Promise resolution. Is it correct or not?
So my doubt is: have my setAddress() method return a Promise? In my specific case setAddress() method is used to call a service method saving some data on the database and have this code:
async setAddress(): Promise<void> {
try {
const c: Address = {
companyName:this.addressFormGroup.get('companyName').value,
street: this.addressFormGroup.get('street').value,
city: this.addressFormGroup.get('city').value,
zipCode: this.addressFormGroup.get('zipCode').value,
notes: this.addressFormGroup.get('notes').value,
};
//save/update record
await this.userService.setUserAdresss(this.currentUserUID,this.addressType,c);
this.success = true;
if (!this.isEditMode) {
this.addressFormGroup.reset();
}
} catch (e) {
console.error(e);
} finally {
this.submitted.next(false);
}
}
And here I have a lot of doubts on how it works...ok the method signature:
async setAddress(): Promise<void> {
seems to return a Promise (why ? what it means?). But where is it effectivelly returning a Promise? In the code of this method I can't find that it is returning a Promise nowhere. It seems to me that it is returning nothing because it is not containing the return statement !!!
My only interpretation is the following one (but it is my idea and probably totally wrong): also if it is not explicitly returning nothing it have a Promise as method returned type. So it means that at the end of the method execution TypeScript automatically return an "empty" Promise that simply means: "method execution completed".
I am absolutly not sure, this is the only explaination that I can give to this code.
How it wxactly works?
Your assumptions are correct.
A function declared with the async keyword will return a Promise that is completed when the function has finished its execution.
The await keyword is equivalent to using the then method with the remaining lines of codes as the callback function.
Using try/catch/finally arround an await is equivalent to using the catch/finally method on the promise.
This is your code written with promises instead of async/await :
post(): Promise<void> {
this.submitted.next(true);
return this.setAddress()
.then(() => this.activeModal.close())
.catch((e) => {
console.error('Storage upload error', e);
this.submitted.next(false);
});
}
setAddress(): Promise<void> {
const c: Address = {
companyName:this.addressFormGroup.get('companyName').value,
street: this.addressFormGroup.get('street').value,
city: this.addressFormGroup.get('city').value,
zipCode: this.addressFormGroup.get('zipCode').value,
notes: this.addressFormGroup.get('notes').value,
};
//save/update record
return this.userService.setUserAdresss(this.currentUserUID,this.addressType,c)
.then(() => {
this.success = true;
if (!this.isEditMode) {
this.addressFormGroup.reset();
}
})
.catch((e) => console.error(e))
.finally(() => this.submitted.next(false));;
}
I have a list of department, which will be fetched from an API link.
When my angular app loads (I means after ngOnInit(): void call), I want :
I will fetch all department as async
Then I will await for all department to load
Finally, I will save 1st department's id in a variable
I am in confusion where I should put my await
(Note: my code is working, API response comes to me, I am only stuck in the await part)
Here is how far I tried:
app.component.ts:
import { Component, OnInit } from '#angular/core';
import { DepartmentModel } from '../../user/models/individual-department.model';
#Component({
selector: 'app-requisition-report-one',
templateUrl: './requisition-report-one.component.html',
styleUrls: ['./requisition-report-one.component.css']
})
export class RequisitionReportOneComponent implements OnInit {
displayIndividualDepartmentList: DepartmentModel[] = [];
currentDisplayDepartment: number = null;
constructor(private userService: UserService) { }
ngOnInit(): void {
this.initializeDepartmentList();
}
initializeDepartmentList() {
this.userService.GetAllIndividualDepartment().subscribe(
async (data: DepartmentModel[]) => {
this.displayIndividualDepartmentList = data;
this.currentDisplayDepartment = await this.displayIndividualDepartmentList[0].department_id;
}
);
}
onShowButtonClick() {
console.log(this.currentDisplayDepartment);
}
}
My vscode is giving me a warning like this
'await' has no effect on the type of this expression.ts(80007)
Besides, on show button click, I am getting null value.
I want to get the first department's id in this variable currentDisplayDepartment from the list displayIndividualDepartmentList after awaiting for the list to be loaded.
TIA
RxJS observables don't rely on async/await feature, but rather on a subscription/callback pattern. Your callback function will be executed everytimes the observable returned by GetAllIndividualDepartment method from UserService emits a new value, and therefore does not require any async prefix. You can access data in a synchronous way with no need to use the await keyword.
initializeDepartmentList() {
this.userService.GetAllIndividualDepartment().subscribe(
(data: DepartmentModel[]) => {
this.displayIndividualDepartmentList = data;
this.currentDisplayDepartment = this.displayIndividualDepartmentList[0].department_id;
}
);
}
A quick explanation of RxJS observables and how they are different from the Promise you are used to work with.
You can convert your Observable to a promise as below and use the async/await syntax as opposed to .then() syntax.
The toPromise() method will handle the subscribe/unsubscribe for you and will only emit when the observable has completed. In the case of HTTP observables they typically only emit one value (primitive or otherwise) and then complete.
You can then use async/await syntax as shown or use .then().
Using toPromise() with HTTP observeables is a very compelling and clean syntax making asynchronous code look synchronous and easier to follow.
async initializeDepartmentList(): Promise<void> {
const data: DepartmentModel[] = await this.userService.GetAllIndividualDepartment().toPromise();
this.displayIndividualDepartmentList = data;
this.currentDisplayDepartment = this.displayIndividualDepartmentList[0].department_id;
}
Call like this...
async ngOnInit(): Promise<void> {
await this.initializeDepartmentList();
.
.
}
Or with a self invoking function like this.
ngOnInit()
{
(async () => {
await this.initializeDepartmentList();
})()
}
Just to add that toPromise is being deprecated in RxJS 7 and will be removed in RxJS 8.
In its place you can use lastValueFrom
const data = await lastValueFrom(this.userService.GetAllIndividualDepartment());
As a side note, if you want to initialise your application upon startup you can use...
app.module.ts
providers: [
{
provide: APP_INITIALIZER,
useFactory: appInitializeService,
multi: true
},
Something like this. A promise returning void. The app effectively waits for your prerequisites to load before continuing.
export const appInitializeService = (): () => Promise<void> => (): Promise<void> => new Promise((resolve) => resolve());
Let's take the code bellow:
myObservable.subscribe({
next: async () => {
await something();
},
complete: () => {
console.log('All async task are comlpeted');
}
});
The problem is that the console.log is called after the last next is triggered but I want it to be called after the last something() is finished.
Is there any way to do that ?
I specify that I implemented the myObservable by myself using new Obsercable(observer => ...). So it could be modified.
I would either a) stick to the observable recipe, and convert promises to observables, or b) stick to the promise/async-await pattern, and convert observables to promises. I frankly have no idea how to successfully mix those two.
Rx-based solution:
import { from } from 'rxjs';
import { finalize } from 'rxjs/operators';
myObservable.pipe(
switchMap(() => from(something()),
finalize(() => console.log('All async task are comlpeted')),
).subscribe(someFunction);
I want to construct a function that creates and returns an Observable. Depending on the parameters this function receives it should either create the Observable and return it directly or (in a special case) call another asynchronous function before returning the Observable.
Abstract example:
async function doSomethingAsync(){
return 'success';
}
function returnObservable(specialCase: boolean): Observable<any>
const observable = new Observable(
observer =>
observer.next(1)
}
);
if(specialCase) {
doSomethingAsync().then(
then => {
// this does not work, of course, but that's what I would like to be able to do
return observable;
}
)
} else {
return observable;
}
}
Now my problem is that appearently I cannot call an asynchronous function and then return the Observable. I could make this whole returnObservable function asynchronous and just await the doSomethingAsync but then the returnObservable function would return a Promise returning an Observable - and that's not what I want. The consumer of this function should receive the Observable directly.
I hope I could make my problem clear. Is there any way to solve this problem?
Is there any way to solve this problem?
Because you're returning an Observable, there may be.
It's impossible to have returnObservable block synchronously until an async function returns. But, you can have it return an Observable that (optionally) won't emit any events until the async function completes. You could even have it not even call the async function until/unless something subscribes to it.
Something vaguely like:
function returnObservable(specialCase: boolean): Observable<any>
const promise = specialCase ? doSomethingAsync() : Promise.resolve();
const observable = new Observable(
observer => {
promise
.then(() => {
observer.next(1);
})
.catch(error => {
// deal with the error
});
}
);
return observable;
}
(You might even be able to use fromPromise. I'm not strong enough on Rx.js to be sure.)
Transform the promise into an observable:
async function doSomethingAsync(){
return 'success';
}
function returnObservable(specialCase: boolean): Observable<any>
const observable = new Observable(
observer =>
observer.next(1)
}
);
if(specialCase) {
return Observable.from(doSomethingAsync().then(
then => {
// this does not work, of course, but that's what I would like to be able to do
return observable;
}
));
}
return observable;
}
I have an array of items which on each element I want to make an HTTP call, wait for it to finish, then make another call, only one at a time.
I tried:
index(item) {
return this.service.index(item).pipe(
map(response => {
// handle success case
}),
catchError(error => {
// handle error case
})
)
}
async processArray(array) {
const promises = array.map(item => this.index(item));
await Promise.all(promises);
}
proccessArray(array);
Also with NGRX Effects:
#Effect()
effect$ = this.actions$.pipe(
ofType<action>(actionTypes.action),
mergeMapTo(this.store.select(getMyArray)),
flatMap((request: any[]) => {
return zip(...request.map(item => {
return this.service.index(item).pipe(
map(response => {
// handle success case
}),
catchError(error => {
// handle error case
})
)
}))
}),
);
Also tried doing it in for and forEach loops but they fire all the requests at once. How could I achieve this?
If you are using promises and want to wait for each promise to resolve before another call is made then (1) you should not use Promise.all as this will wait til all requests are resolved and (2) you need to use a plain old for-loop which enables you to wait for async operations within the loop.
async processArray(array) {
for(var i = 0; i < array.length; i++){
await yourServiceCall();
}
}
As a sidenote: Since you are using async-await, don't forget to convert your observables to promises.
If you want to move away from promises (and async-await) and rely on pure RxJS instead, have a look at concatMap:
Projects each source value to an Observable which is merged in the output Observable, in a serialized fashion waiting for each one to complete before merging the next.
For example:
import { from } from 'rxjs/observable/from';
ngOnInit() {
from(myArray)
.pipe(concatMap(el => yourServiceCall(el)))
.subscribe(/* your logic */);
}