promise.all not access in rejection function - javascript

i have one service that get data , and i called it 5 times with different parametes to get different data.
I called a function to execute in success case : it work fine.
but in case of failure one from the 5 calls i need to do something else what's not happen : it always enter in success function.
I'm using ionic 4 angular 2
this is the service i have :
public getdataLookUps(type, lcid): Promise<string[]> {
return new Promise((resolve, reject) => {
if (this.data[type + lcid]) {
resolve(this.data[type + lcid]);
return;
}
this.authService.getToken().then(
(accessToken) => {
let headers = new Headers({'Authorization': 'bearer ' + accessToken});
let url = 'error url to test failure case';
this.http.get(url, {headers: headers})
.map(res => res.json())
.toPromise()
.then(
(res) => {
this.data[type + lcid] = res;
resolve(res);
},
(error) => {
reject(error);
}
);
}
);
});
}
then I wrapper the function that calls the service like this: ( repeated 5 times with different params):
public getAreas() {
return this.lookupsService.getdataLookUps('Region', this.lcid).then(
(res) => {
this.areas = res;
},
() => {
//todo
return Promise.reject('rejection error');
}
);
}
then I call the 5 functions :
ngOnInit() {
this.getCaseCategories();
this.getAreas();
this.getWeather();
this.getMifonProjects();
this.getUserInfo();
}
and I do promise.all() here :
ngAfterViewInit(){
Promise.all(
[
this.getCaseCategories(),
this.getAreas(),
this.getWeather(),
this.getMifonProjects(),
this.getUserInfo(),
]
).then(
() => {
this.loadMap();
},
() => {
this.showErrorMessage = true;
}
);
}

This code has two callbacks for then, a success handler, and an error handler. If the code is as you have shown the error handler returns a success result so your Promise.all() will always succeed:
public getAreas() {
return this.lookupsService.getdataLookUps('Region', this.lcid).then(
(res) => {
this.areas = res;
},
() => {
//todo
}
);
}
Don't add an error handler unless you are really able to handle the error here. Instead just let the error propagate out to the next handler:
public getAreas() {
return this.lookupsService.getdataLookUps('Region', this.lcid)
.then(res => this.areas = res);
}
Now your Promise.all will give you an error when the data lookup fails.
Also stop nesting your promise handlers:
public getdataLookUps(type, lcid): Promise<string[]> {
if (this.data[type + lcid]) return Promise.resolve(this.data[type + lcid]);
return this.authService.getToken().then(
(accessToken) => {
let headers = new Headers({'Authorization': 'bearer ' + accessToken});
let url = 'error url to test failure case';
return this.http.get(url, {headers: headers})
.map(res => res.json())
.toPromise();
})
.then((res) => this.data[type + lcid] = res);
}
Once you have a Promise just return the Promise there is no need to create a new Promise. And if your promise success handler creates another promise return that to avoid nesting. Your error handler did nothing but propagate the error, so when you don't have the nested promise you don't need that either, just let the error propagate naturally.

I solved it by removing the calls of the functions in ngOnInit();
and keep everything same as my example above (not change anything in the getDataLookUps service)

Related

Can not return from a function

I have a function that looks like following
export const checkForAvailableAgent = (topicId, serviceUrl, serviceId) => {
const serviceInfo = new window.adiaLive.ServiceInfo({
topicId: topicId, // set here the topicId which you want listen for
OnError: e => {
// react to error message (optional)
console.log("error: ", e);
},
OnServiceStateChange: e => {
if (e.ConnectedAdvisers > 0) {
// there are advisers online for given topicId
console.log("studio available");
return true;
} else {
console.log("studio not available");
return false;
}
}
});
serviceInfo.connect(serviceUrl, serviceId);
};
however the return statements don't return anything when I use the function in the following manner
useEffect(() => {
const agent = checkForAvailableAgent(
`sales_${i18n.language}`,
"https://linktoserviceurl",
"serviceid"
);
// console.log("studio available is: ", agent);
}, []);
the console.log massages appear but the return statement is undefined.
any help would be appreciated.
You can not return from a callback function, as it is running asynchronously and you are not waiting for it to have a result ready.
You can however make the function itself async by returning a Promise instead of the actual result and wait until the Promise has a result ready (e.g. it is resolved):
export const checkForAvailableAgent = (topicId, serviceUrl, serviceId) => {
return new Promise((resolve, reject) => {
const serviceInfo = new window.adiaLive.ServiceInfo({
topicId: topicId, // set here the topicId which you want listen for
OnError: e => {
// react to error message (optional)
console.log("error: ", e);
reject(); // reject on failure
},
OnServiceStateChange: e => {
if (e.ConnectedAdvisers > 0) {
// there are advisers online for given topicId
console.log("studio available");
resolve(true); // resolve instead of return
} else {
console.log("studio not available");
resolve(false);
}
}
});
serviceInfo.connect(serviceUrl, serviceId);
})
};
useEffect(() => {
checkForAvailableAgent(
`sales_${i18n.language}`,
"https://linktoserviceurl",
"serviceid"
).then((agent) => { // then callback is called when the promise resolved
console.log("studio available is: ", agent);
}).catch(error => { // catch is called when promise got rejected
console.log('An error happened');
});
}, []);
The function servceInfo.OnServiceStateChange is a function into the object (seems to be an event).
I'd suggest declaring a variable on the checkForAvailableAgent like connected and change it's value when the event is called.
Then access it using checkForAvailableAgent.connected.
A version with async/await and try/catch
export const checkForAvailableAgent = (topicId, serviceUrl, serviceId) => {
return new Promise((resolve, reject) => {
const serviceInfo = new window.adiaLive.ServiceInfo({
topicId: topicId,
OnError: reject,
OnServiceStateChange: e => resolve(e.ConnectedAdvisers > 0)
});
serviceInfo.connect(serviceUrl, serviceId);
})
};
useEffect(() => {
(async () => {
try {
const isAvailable = await checkForAvailableAgent(
`sales_${i18n.language}`,
"https://linktoserviceurl",
"serviceid"
);
// console.log("Result", isAvailable)
} catch(e) {
console.error(e)
}
})()
// console.log("studio available is: ", agent);
}, []);
There are 2 possible reasons
you are not returning anything from checkForAvailableAgent.
After returning from the checkForAvailableAgent, it might be asynchronous function. You can use async & await.

axios - how to chain axios calls within a single promise

I am still very new to Axios and promises. I'm close to understanding this, but I know I am doing some things wrong. I have a javascript method that is supposed to return a promise. Inside that method, I have an Axios post with two .then methods chained onto it. If my initial post fails, I get this ugly error in the console: Unhandled promise rejection ReferenceError: "reject is not defined". I have a feeling I shouldn't be nesting the .catch methods like I am. I'm thinking it should simply be post.then.then.catch.
Additionally, can anyone see why I'm not getting itemInformation being sent back in the response in the second .then?
Here is the relavant Javascript code(the addToCartVue method gets called first):
addToCartVue(itemData) {
let vm = this;
return new Promise((resolve, reject) => {
vm.buildDataString(itemData);
axios.post(POST_ENDPOINT, {
data: vm.dataString
},
{
/*headers: {
"Content-Type": "application/x-www-form-urlencoded"
}*/
}).then(response => {
return vm.updateCartInfo(vm.dataString, itemData.addToCartParameters.itemId, vm.selectedStoreId, vm.quantity);
}).then(response => {
if (itemData.addToCartParameters.showLB) {
vm.emitGlobalEvent('addToCart::open', itemData);
resolve(response);
}
}).catch(error => reject(error));
}).catch(error => reject(error));
}, // end of addToCartVue method
buildDataString(itemData) {
// irrelevant code that builds quantity and dataString variables
vm.quantity = quantity;
vm.dataString = dataString;
}, // end of buildDataString method
updateCartInfo(dataString, itemId, selectedStore, quantity) {
axios.get(GET_ENDPOINT, {
params: {
data: dataString
}
}).then(response => {
cartDropDown.populateCartDropDown(response);
const addedItem = response.addedItem;
const basketInfo = response.basketInfo;
let productQuantity = quantity;
if (addedItem.quantity > -1) {
productQuantity = addedItem.quantity;
}
const itemInformation = {
"itemId": itemId,
"selectedStore": selectedStore,
"addedItem": addedItem,
"basketInfo": basketInfo,
"displayValues": null,
"quantity": productQuantity,
"isCustomProduct": false
};
return itemInformation;
}).catch(err => error => reject(error));
} // end of updateCartInfo method
I think the issue is missing 'return' keyword.
Try adding return in two places.
return axios.post(POST_ENDPOINT...
And also inside updateCartInfo,
return axios.get(GET_ENDPOINT,...
Also, i don't think you need to wrap you code inside a Promise object since axios already returns a promise.This will avoid reject reference error.
let vm = this;
vm.buildDataString(itemData);
return axios.post(POST_ENDPOINT, {
data: vm.dataString
},
{
/*headers: {
"Content-Type": "application/x-www-form-urlencoded"
}*/
}).then(response => {
return vm.updateCartInfo(vm.dataString, itemData.addToCartParameters.itemId, vm.selectedStoreId, vm.quantity);
}).then(response => {
if (itemData.addToCartParameters.showLB) {
vm.emitGlobalEvent('addToCart::open', itemData);
return response
}
})
And catch your errors in the call to
addVue().then(data => console.log(data).catch(err => console.log(err))

Axios prints value on console but returns undefined

I have quite an issue for some time and is getting on my nerves and it doesn't make sense. I have used axios on my react frontend and it works perfect when assigning the get value to the state. But when using it in a normal javascript code, I appear to have this following issue: i can print the object's value in the console but it will return only undefined.. Here is my code:
login = () => {
let data;
axios.get('https://myaddress/authenticate')
.then(response => {
data = response;
console.log('data here', data);
})
.catch(error => {
console.error('auth.error', error);
});
console.log('eee', data);
return data;
};
Here we are talking about axios strictly.
You can't return an ajax response because it's asynchronous. You should wrap your function into a promise or pass a callback to login
UPDATE: As #Thilo said in the comments, async/await would be another option, but it will let you set the response to data tho ...
1. Wrap into a promise
login = () => new Promise((resolve, reject)=>{
axios.get('https://myaddress/authenticate')
.then(response => {
resolve(response)
})
.catch(error => {
reject(error)
});
});
// Usage example
login()
.then(response =>{
console.log(response)
})
.catch(error => {
console.log(error)
})
2. Pass a callback
login = (callback) => {
axios.get('https://myaddress/authenticate')
.then(response => {
callback(null,response)
})
.catch(error => {
callback(error,null)
});
};
// Usage example
login((err, response)=>{
if( err ){
throw err;
}
console.log(response);
})
3. Async/Await
login = async () => {
// You can use 'await' only in a function marked with 'async'
// You can set the response as value to 'data' by waiting for the promise to get resolved
let data = await axios.get('https://myaddress/authenticate');
// now you can use a "synchronous" data, only in the 'login' function ...
console.log('eee', data);
return data; // don't let this trick you, it's not the data value, it's a promise
};
// Outside usage
console.log( login() ); // this is pending promise
In ES7/ES8 you can do async/await like a boss:
login = () => {
return new Promise((resolve, reject) => {
axios.get('https://myaddress/authenticate')
.then(response => {
resolve(response)
})
.catch(error => {
console.error('auth.error', error);
reject(error)
});
});
};
async function getData() {
try{
const data = await login()
} catch(error){
// handle error
}
return data;
}
getData()
.then((data) => console.log(data));

Testing HttpClient call with callFake()

I am trying to create a spec to test a method in my Angular service that makes a GET request. The difficulty I am having is mocking the method to get it to return an error instead of the response. If I cannot get it to return an error (such a 400 or 500 for example) I cannot provide full code coverage...
Code being tested:
maingrid.service.ts:
async loadAccountListPromise(id: string) {
let queryParams = `?emailAddress=${id}`;
let promise = new Promise((resolve, reject) => {
this.http.get(`${this.baseUrl}` + queryParams, { responseType: 'json' })
.toPromise()
.then(
(data) => {
this.results = this.formatData(data);
resolve(this.results);
},
(err) => {
this.logService.error('loadAccountListPromise() exception:', err);
this.setError(this.message[0], err);
reject('loadAccountListPromise() exception');
}
);
});
return promise;
}
setError(errorMessage: string, errorCode?: string): void {
this._error.next(new NxpError(errorMessage, 'AccountListService',
errorCode));
}
clearError(): void {
this._error.next(null);
}
This is the spec I have attempted to write to mock the method using callFake():
maingrid.service.spec.ts
it('logs and sets a local error for system errors/exceptions', () => {
let id: string = 'ppandya#pershing.com';
let myUrl = 'https://localhost:9999/...';
let queryParams = `?emailAddress=${id}`;
spyOn(httpClient, 'get').and.callFake( loadAccountListPromise( (response) => {
// need to return error here...somehow
}));
spyOn(logService, 'error');
spyOn(maingridService, 'setError');
maingridService.loadAccountListPromise(id);
let request = httpMock.expectOne(myUrl + queryParams);
expect(request.request.method).toEqual('GET');
httpMock.verify();
expect(logService.error).toHaveBeenCalled();
expect(maingridService.setError).toHaveBeenCalled();
});
I am not sure what I need to do to properly mock the loadAcountListPromise() method so that it enters the error block and calls the setError() and logService.error() methods.
Try to use the 'spyOn()' and return a throw like this:
spyOn(httpClient, 'get').and.returnValue(Observable.throw({status: 404}));
//Observable.throw(new Error(`Error: ${error}`));

Angular 2 Observables

I'm building an app to get some events from facebook, take a look:
EventComponent:
events: Object[] = [];
constructor(private eventService: EventService) {
this.eventService.getAll()
.subscribe(events => this.events = events)
}
EventService:
getAll() {
const accessToken = 'xxxxxxxxxxx';
const batch = [{...},{...},{...},...];
const body = `access_token=${accessToken}&batch=${JSON.stringify(batch)}`;
return this.http.post('https://graph.facebook.com', body)
.retry(3)
.map(response => response.json())
}
AuthenticationService:
getAccessToken() {
return new Promise((resolve: (response: any) => void, reject: (error: any) => void) => {
facebookConnectPlugin.getAccessToken(
token => resolve(token),
error => reject(error)
);
});
}
I have a few questions:
1) How can I set an interval to update the events every 60 seconds?
2) The value of accessToken will actually come from a promise, should I do something like this?
getAll() {
const batch = [{...},{...},{...},...];
this.authenticationService.getAccessToken().then(
accessToken => {
const body = `access_token=${accessToken}&batch=${JSON.stringify(batch)}`;
return this.http.post('https://graph.facebook.com', body)
.retry(3)
.map(response => response.json())
},
error => {}
);
}
3) If yes, how can I also handle errors from the getAccessToken() promise since I'm returning just the Observer?
4) The response from the post request will not return an array of objects by default, I'll have to make some manipulation. Should I do something like this?
return this.http.post('https://graph.facebook.com', body)
.retry(3)
.map(response => response.json())
.map(response => {
const events: Object[] = [];
// Manipulate response and push to events...
return events;
})
Here are the answers to your questions:
1) You can leverage the interval function of observables:
getAll() {
const accessToken = 'xxxxxxxxxxx';
const batch = [{...},{...},{...},...];
const body = `access_token=${accessToken}&batch=${JSON.stringify(batch)}`;
return Observable.interval(60000).flatMap(() => {
return this.http.post('https://graph.facebook.com', body)
.retry(3)
.map(response => response.json());
});
}
2) You could leverage at this level the fromPromise function of observables:
getAll() {
const batch = [{...},{...},{...},...];
return Observable.fromPromise(this.authenticationService.getAccessToken())
.flatMap(accessToken => {
const body = `access_token=${accessToken}&batch=${JSON.stringify(batch)}`;
return this.http.post('https://graph.facebook.com', body)
.retry(3)
.map(response => response.json())
});
}
3) You can leverage the catch operator to handle errors:
getAll() {
const batch = [{...},{...},{...},...];
return Observable.fromPromise(this.authenticationService.getAccessToken())
.catch(() => Observable.of({})) // <-----
.flatMap(accessToken => {
const body = `access_token=${accessToken}&batch=${JSON.stringify(batch)}`;
return this.http.post('https://graph.facebook.com', body)
.retry(3)
.map(response => response.json())
});
}
In this case, when an error occurs to get the access token, an empty object is provided to build the POST request.
4) Yes sure! The map operator allows you to return what you want...
Put the event inside a timeout block and set the interval of 60s. setTimeout(() => {},60000).
Using Template string is totally fine but you're telling its value comes from a promise. If your whole block of code is inside resolve function of promise this should fine. So it depends on where your code is. And why promises .In A2 it's recommended to use Observables and not promises. Don't mix them.
You're not returning anything in the error function. So if you return error from that block you'll get error data in case of error. error => erroror error => { return error; }.
Exactly you should you map to get the response and manipulate it and return just the array from that function. .map(resp => { return resp.array}). Since respons is in JSON format now you have to get array from it and return it. you can do as much modifications you want before returning it.
Feel free to edit the answer...

Categories