axios - how to chain axios calls within a single promise - javascript

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))

Related

I am getting error while calling functions inside mapped array in React JS

Actually I am calling function by mapping over array
let artistId = artist._id;
orderData.products.map((cartproduct, index) => {
const sendOrderData = {
product: cartproduct._id,
qty: cartproduct.qty,
};
createOrder(userId, sendOrderData).then((data) => {
if(data.error){
console.log(data.error)
}else{
updateOrderDetails(artistId,sendData).then((response) => {
if(response.error){
console.log(response.error)
}else{
console.log("done)
}
})
But before updateOrderDetails function call is completed for first element the createorder function is called for next element and thus I am not able to update data adequately.
UpdateOrderDetails makes an API call:
export const updateOrderDetailsOfArtist = (artistId, data) => {
return fetch(`${API}/artist/updateorderdetails/${artistId}`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(data),
})
.then((response) => {
return response.json();
})
.catch((err) => console.log(err));
};
So, where ever you have the updateOrderDetails(artistId) function written,
add async to it.
async function updateOrderDetails(artistId) {
...
// where ever you're using the return statement type await
return await response
}
But for a better answer you need to show what the updateOrderDetails function does, what does it return, what API calls it is doing.

How to add a new then after fetch has run

I have a method that runs a fetch request and then saves the result or error like this:
saveTema() {
this.gateway.editTema(this.state.tema)
.then(tema => {
this.setState({
tema,
error: null,
isDirty: false,
});
})
.catch(httpOrOtherError => {
if (httpOrOtherError.status) {
if (httpOrOtherError.status === 400) {
httpOrOtherError.json().then(result => {
const serverValidationfailures =
this.transformValideringsfeil(result.valideringsfeil);
this.setState({
error: {
valideringsfeil: {...serverValidationfailures},
},
showActivationDialog: false,
})
});
} else {
this.setState({
error: {httpError: {status: httpOrOtherError.status, statusText: httpOrOtherError.statusText}},
showActivationDialog: false,
});
}
} else {
this.setState({
error: {fetchReject: {message: httpOrOtherError.message}},
showActivationDialog: false,
})
}
})
}
And this is the fetch request itself:
editTema(tema) {
return fetch(
this.temaUrl(tema.id),
{
method: 'PUT',
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(tema)
})
.then(res => {
if (res.ok) {
return res.json();
}
throw res;
}
);
}
I would like to run this method from another one, and check if everything went ok with this method and based on that do further actions. Something like this:
this.saveTema().then(() => {
this.props.history.push({
pathname: '/tema',
state: {
successMessage: `Tema ${this.state.tema.id} ble oppdatert`,
}
}}));
But, this is of course wrong, I am not sure how can I do this, to run some code after the fetch handling of the fetch request has finished. What is the right way to do it?
saveTema() {
return this.gateway.editTema(this.state.tema)
...
Return the promise and then you'll be able to do exactly what you are trying to do.
Return the editThema result after setting up the handlers:
saveTema() {
let prom = this.gateway.editTema(this.state.tema)
prom.then(tema => {
// .. success handling code
})
.catch(httpOrOtherError => {
// .. error handling code
})
return prom;
}
Now you can call your function exactly like you wanted to.
You can achieve that by two approaches
Using async/await
Using native Promise
1. async/await way
userController.js
const userUtils = require('./userUtils');
const userCtr = {};
userCtr.searchUser = async (req, res) => {
try {
const { userName } = req.query;
const result = await userUtils.searchUser(userName);
return res.status(200).json(result);
} catch (err) {
return res.status(err.code).json({ error: err.error });
}
};
module.exports = userCtr;
userUtils.js
const userUtils = {};
userUtils.searchUser = async (userName) => {
try {
if (userName) {
// ...Do some cool stuff
const result = [];
return result;
}
const errorObj = { code: 400, error: 'ERR_VALID_PARAM' };
throw errorObj;
} catch (err) {
console.error(err);
throw err;
}
};
module.exports = userUtils;
2. Promise way
userController.js
const userUtils = require('./userUtils');
const userCtr = {};
userCtr.searchUser = (req, res) => {
const { userName } = req.query;
userUtils.searchUser(userName)
.then((result) => {
return res.status(200).json(result);
})
.catch((err) => {
return res.status(err.code).json({ error: err.error });
});
};
module.exports = userCtr;
userUtils.js
const userUtils = {};
userUtils.searchUser = (userName) => {
return new Promise((resolve, reject) => {
if (userName) {
// ...Do some cool stuff
const result = [];
return resolve(result);
} else {
const error = { code: 400, error: 'Please provide valid data!' }
return reject(error);
}
});
};
module.exports = userUtils;
In both approaches you can hold further execution (in both approach Promise are used directly or indirectly), In a second approach you can achieve by .then().catch() whereas in the first approach just you need to put a keyword await and put async on your function, I suggest you to use async/await. Because when you need to wait for the completion of more than 3 promises and yo go with Native Promise then your code will be so messy like .then().then().then() Whereas in a first approach you just need to put a keyword await on starting of your function, Using async/await approach your code will neat and clean and easily understandable and easy to debug.

Correct usage of Promises, Fetch, success/fail handlers

I want to be sure I'm using Promises correctly. We have the below method to simplify making API requests. I've removed things not necessary, like headers.
The intention here is to return a promise to the caller of makeApiRequest. This is being used in the context of a thunk (specifically redux-thunk) and we want to be able to chain more .then() statements.
const makeApiRequest = (request, onSuccess = defaultOnSuccess, onFailed = defaultOnFailed) => {
const CSRF_TOKEN = $('meta[name="csrf-token"]').attr('content')
const headers = { ... }
return fetch(request.url, {...}).then(response => {
if (response.ok) {
return response.json()
.then((json) => Promise.resolve(onSuccess(response.status, json)))
.catch((error) => Promise.reject({message: error, status: response.status}))
}
const errorResponse = {
status: response.status,
message: response.error
}
if(response.status === 401) {
errorResponse.message = 'Unauthorized'
}
return Promise.reject(errorResponse)
}).catch((error) => {
onFailed(error.status, error.message)
console.log(error)
})
}
const defaultOnFailed = (status, error) => console.log([status, error])
const defaultOnSuccess = (status, data) => console.log([status, data])
export default makeApiRequest
Questions
1) Am I implementing success/fail handlers optimally?
2) I feel like having the nested catch statements is wrong, but I need to be able to deal with errors in context.

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}`));

promise.all not access in rejection function

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)

Categories