Angular make an api call async and use the result - javascript

I'm trying to make an API call
ngOnInit(){
this.function1()
}
function1(){
this.userService.getUser()
.then((data) => {
this.user = data.user
this.userM = data.userM
// here problem: If I make a console.log(this.userM) it result at the beginning empty
if(this.userM.length > 0 ) {
console.log("goInside")}
else {
console.log("doesn't enter")
//it enters always there
}
}
}
in my service:
async getUser(): Promise<any>{
let user: any = []
let userM: any = []
//.... operation to populate
return {user, userM}
}
When I make an api call I would to use the this.userM array to make some operations. But it results always empty at the beginning, but it doens't enter anymore in if. How can I do?

Make use of rxjs, try using an Observable instead:
import { of } from 'rxjs';
getUsers(): Observable<any> {
let user: any = []
let userM: any = []
//.... operation to populate
return of({user, userM});
}
And then, within your component:
ngOnInit(){
this.function1()
}
function1(){
this.userService.getUser().subscribe((response) => {
const { user, userM } = response;
if (userM.length) console.log('There is data in it!')
else console.log('No data found...');
});
}
I suggest not using Promises in an Angular project unless necessary, you have at your disposal rxjs along with tons of useful operators for handling async calls/streams of data

Related

How to use async await with subscribe angular

I'm calling createOtherKind() function and trying to use the value of this.listKinds. My problem is that isKindOtherCreated is not waiting this.getKinds() to finish and this.listKinds is undefined.
How can i do it?
Code:
getKinds(): void {
this.detailsService.getKinds().subscribe(async response =>{
this.listKinds = await response.body;
})
}
async createOtherKind() {
await this.getKinds();
const isKindOtherCreated = this.listKinds.find(kind => kind.name === "Other");
if(!isKindOtherCreated) {
this.createdKind.name = "Other";
this.createKind();
}
}
You can call the createOtherKind() method inside getKinds() method. This is how you will get the value from this.listKinds variable. subscribe is async method. So, it does not wait for the reponse.
Here is Example:
getKinds(): void {
this.detailsService.getKinds().subscribe(async response =>{
this.listKinds = response.body;
await this.createOtherKind();
})
}
async createOtherKind() {
const isKindOtherCreated = this.listKinds.find(kind => kind.name === "Other");
if(!isKindOtherCreated) {
this.createdKind.name = "Other";
this.createKind();
}
}
The issue is happening because you're mixing Observables (where you have subscribe) with async/await. If you want to stick to using promises you could revise your getKinds function to something like this:
getKinds(): void {
// Convert your observable to a Promise so you can use `await` on it
return this.detailsService.getKinds().toPromise().then((response) => {
this.listKinds = await response.body;
});
}
For more details, I recommend taking a look at this similar question.
You use subscribe here, so I guess this.detailsService.getKinds() returns an observable. You can make things with it then return it to be able to subscribe something else in another part of your code. like:
getKinds(): Observable<any> {
let myObs = this.detailsService.getKinds(); //Getting your observable
//Reading result
myObs.subscribe(response => {
this.listKinds = response.body;
});
//Returning it to use it somewhere else
return myObs;
}
createOtherKind() {
//Make request and subscribe to observable
this.getKinds().subscribe( res => {
const isKindOtherCreated = this.listKinds.find(kind => kind.name === "Other");
if(!isKindOtherCreated) {
this.createdKind.name = "Other";
this.createKind();
}
}
}
EDIT
As said by Andres2142, subscribe twice isn't exactly the right way to act. It should work but the result isn't perfectly stable, the best way is to replace the first subscribe by a tap pipe since it is desinged to perform side-effects. So a better answer is:
getKinds(): Observable<any> {
return this.detailsService.getKinds()
.pipe(
tap( (response) => {
this.listKinds = response.body;
})
);
}
// createOtherKind() Doesn't need to change

Use Promise function in Angular 6 Data Binding

I'm having an issue where I am using an async function in Angular 6 data binding:
<span *ngIf="application.status == 'in-progress'; else new">
{{getAssignedUser(application.user_id)}}
</span>
The getAssignedUser() is an async function which fetches a document from Firestore and I want to display the assigned user's firstname which I received from Firestore. The only problem here is I can't display the firstname value OnInit. If I insert a button and add a click event, it displays the name.
Component:
async getAssignedUser(id): Promise<string> {
if (id != null) {
return this._usersService
.getUserById(id)
.then(data => {
this.assignedUser = data.user_fname;
return this.assignedUser;
})
} else {
return null;
}
}
Service:
getUserById(id): any {
let user: any;
user = this.afs.collection('agents').doc(id).ref.get().then(function (doc) {
if (doc.exists) {
user = doc.data();
console.log(user);
return user;
}
else {
console.log('No such document');
}
}).catch(function (error) {
console.log('Error getting document: ', error);
})
return user;
}
Any help please?
Issue
The issue with making the call getAssignedUser from the html and expecting to return the value. This will not guarantee because getAssignedUser performs async operation. You had mentioned the async operation on getAssignedUser however does not returns any Observable or Promise.
Solution
You need to change in both services and component. Function should return the Promise to handle this case.
Service:
getUserById(id): any {
return new Promise((resolve, reject) => {
this.afs.collection('agents').doc(id).ref.get().then(function (doc) {
if (doc.exists) {
user = doc.data();
console.log(user);
resolve(user);
}
else {
resolve(null); //<-- you can reject if you want.
}
}
}
}
Component:
async getAssignedUser(id): Promise<string> {
return this._usersService
.getUserById(id);
}
html
<span *ngIf="application.status == 'in-progress'; else new">
{{getAssignedUser(application.user_id)} | async}
</span>
Important : You should not function in html, it may lead to multiple call and will impact on the performance. Better would be to use function instead.
Note : code is written directly to stackoverflow editor so there could be typo or syntactical error. So please correct yourself.
Agree with #Sunil Singh Above Answer, small correction in template file. async operator is mainly used for observable. You can call without that it should work.
<span *ngIf="application.status == 'in-progress'; else new">
{{getAssignedUser(application.user_id)}}
</span>

Async/Await not working for VueX getter but works for log

I have an object of convos with userIDs that I need to loop through and, inside the loop, I need to make a call to Firebase to get the corresponding userName and then return an object with the convos, userNames, and userIDs.
I have tried using the async/await and the result I get from console.log is correct but my return statement directly after that, is undefined. Why is this happening? They are receiving the same object.
store.js getter snippet
getConvosObj: state => {
var convoObj = {};
var userConvos = state.userProfile.convos;
async function asyncFunction() {
for (const key in userConvos) {
if (userConvos.hasOwnProperty(key)) {
const userID = userConvos[key];
var userName;
await fire.database().ref('/users/' + userID + '/userName').once('value', async (snapshot) => {
userName = await snapshot.val();
convoObj[key] = {userName, userID}
})
}
}
console.log(convoObj); //result: correct object
return convoObj; //result: undefined
}
asyncFunction();
}
Why is this happening ?
Because you called async function synchronously.
lets make your code simpler.
getConvosObj: state => {
async function asyncFunction() {
// ...
}
asyncFunction();
}
at this point, your getConvosObj() will return nothing, because getConvosObj() ends before asyncFunction() ends.
you need to wait until your asyncFunction() ends, then your code should be like this:
getConvosObj: async state => { // <- changed here
async function asyncFunction() {
// ...
}
await asyncFunction(); // <- changed here too
}
but you should not do like this, because getters are not meant to be asynchronous by design.
this may work, but you should try a different approach.
So what should you do ?
Use actions before using getters
this is a basic approach.
async functions should be in actions.
so your store should be like this:
export default () =>
new Vuex.Store({
state: {
convoObj: null
},
mutations: {
updateConvoObj(state, payload) {
state.convoObj = payload;
}
},
actions: {
async fetchAndUpdateConvoObj({ state, commit }) {
const fetchUserData = async userId => {
const snapShot = await fire.database().ref('/users/' + userID + '/userName').once('value');
const userName = snapShot.val();
return {
userName: userName,
userID: userId
}
}
const userConvos = state.userProfile.convos;
let convoObj = {};
for (const key in userConvos) {
if (userConvos.hasOwnProperty(key)) {
const userId = userConvos[key];
const result = await fetchUserData(userId);
convoObj[key] = {
userName: result.userName,
userId: result.userId
}
}
}
commit('updateConvoObj', convoObj);
}
}
});
then call your actions before using getter in your sample.vue:
await this.$store.dispatch('fetchAndUpdateConvoObj');
convoObj = this.$store.getters('getConvoObj');
wait for db and update store, then get its state.
doesnt make sense ?
Use vuexfire to connect your store directly to Realtime Database
another approach is this.
use vuexfire, then the state of the store is always up-to-date to realtime database, so you can call getters without calling actions.
i got tired to refactor / write a code, so google some sample if you wanna use that plugin :)
i refactored the original code a lot, so there should be some typo or mistake.
plz revise is if you find one.

How to properly implement mongodb async/await inside a promise?

I've read that having an async inside a Promise is anti-pattern for async/await. The code below works, but I am curious how else to achieve the same result without having async in Promise.
If I remove it, the linter would tell how I can't use await in my mongodb query. If I remove the await in the mongodb query, then it wouldn't wait for the result.
export const getEmployees = (companyId) => {
return new Promise(async (resolve, reject) => {
const employees = await Employees.find(
{ companyId },
);
// other logic here...
resolve({
employees,
});
});
Thanks.
async functions automatically return Promises already, which resolve with whatever expression is eventually returned. Simply make getEmployees an async function:
export const getEmployees = async (companyId) => {
const employees = await Employees.find(
{ companyId },
);
// other logic here...
return { employees };
};
(but make sure to catch in the consumer of getEmployees just in case there's an error)
As #CertainPerformance answered, that is perfect way to retrieve data from mongoDB using async/await, I would like to add some more information about how to handle errors in this case for correctness of the system, and better error handle to return better status to the client about his request.
I'd say it , you usually want to catch all exceptions from async/await call.
try {
const employees = await Employees.find({
companyId
});
// You can add more logic here before return the data.
return {
employees
};
} catch (error) {
console.error(error);
}
Now let's check the ways we can handle our errors that might occur.
Handle error inside error scope.
Assign a default value to the variable in the catch block.
Inspect error instance and act accordingly.
This is the most common way to handle errors in those cases and most elegant way in my opinion.
Handle error inside error scope:
export const getEmployees = async (companyId) => {
try {
const employees = await Employees.find({
companyId
});
// You can add more logic here before return the data.
return {
employees
};
} catch (error) {
console.error(error);
}
};
Assign a default value to the variable in the catch block:
export const getEmployees = async (companyId) => {
let employees;
try {
employees = await Employees.find({
companyId
});
// You can add more logic here before return the data.
employees = employees;
} catch (error) {
console.error(error);
}
if (employees) { // We received the data successfully.
console.log(employees)
// Business logic goes here.
}
return employees;
};
Inspect error instance and act accordingly:
export const getEmployees = async (companyId) => {
try {
const employees = await Employees.find({
companyId
});
// You can add more logic here before return the data.
return {
employees
};
} catch (error) {
if (error instanceof ConnectionError) {
console.error(error);
} else {
throw error;
}
}
};
Some more explanations about async await and more useful methods that you can find in those answers.
How run async / await in parallel in Javascript

calling an async function in the constructor.

getUser is an async function? if it is going to take longer time to resolve? is it going to always return the right value in my someotherclass.
class IdpServer {
constructor() {
this._settings = {
// some identity server settings.
};
this.userManager = new UserManager(this._settings);
this.getUser();
}
async getUser() {
this.user = await this.userManager.getUser();
}
isLoggedIn() {
return this.user != null && !this.user.expired;
}
}
let idpServer = new IdpServer();
export default idpServer;
// another class
// import IdpServer from '...'
class SomeOtherClass {
constructor() {
console.log(IdpServer.isLoggedIn());
}
}
This is a problem that is related to this popular question.
Once a code is asynchronous, it cannot be used in synchronous manner. If the use of raw promises is unwanted, all control flow should be performed with async functions.
The problem here is that getUser provides a promise of user data, not user data itself. A promise is lost in constructor, and this is antipattern.
One way to solve the problem is to provide initialization promise for IdpServer, while the rest of API will be synchronous:
class IdpServer {
constructor() {
...
this.initializationPromise = this.getUser();
}
async getUser() {
this.user = await this.userManager.getUser();
}
isLoggedIn() {
return this.user != null && !this.user.expired;
}
}
// inside async function
await idpServer.initializationPromise;
idpServer.isLoggedIn();
Depending on how the application works, IdpServer.initializationPromise can be handled on application initialization to guarantee that all units that depend on IdpServer won't be initialized until it's ready.
Another way is to make IdpServer entirely asynchronous:
class IdpServer {
constructor() {
...
this.user = this.getUser(); // a promise of user data
}
async getUser() {
return this.userManager.getUser();
}
async isLoggedIn() {
const user = await this.user;
return user != null && !user.expired;
}
}
// inside async function
await idpServer.isLoggedIn();
It's expected that all units that depend on it will also have asynchronous API.

Categories

Resources