Promise continues after error - javascript

I have some async code that needs to stop in case of error but keeps executing:
async saveCoupons({ state, rootState, dispatch, commit }) {
const promises = []
state.userCoupons.forEach(coupon => {
if (coupon.isNew && coupon.isUpdated) {
// if the user is creating a new coupon
promises.push(Vue.axios.post('/api_producer/coupons.json', coupon, { params: { token: coupon.token } }))
} else if (!coupon.isNew && coupon.isUpdated) {
// if the user is updating the coupon
promises.push(Vue.axios.patch(`api_producer/coupons/${coupon.id}/`, coupon, { params: { token: coupon.token } }))
}
})
try {
await Promise.all(promises)
dispatch('utilities/showModal', 'success', { root: true })
dispatch('fetchProducerCoupons')
} catch (err) {
let couponToken = err.request.responseURL.split('token=')[1]
commit('ADD_ERROR_ON_COUPON', couponToken)
console.log(err)
}
}
This is how the code is currently structured, it works, but I realize it's terrible. What I need to do is stop the excution of
dispatch('utilities/showModal', 'success', { root: true })
dispatch('fetchProducerCoupons')
In case one of the api calls fails. I wanted to catch the error inside the forEach so I already have the item available and I can add the error to it right away as opposed to doing it after (which is what I'm doing now with { params: { token: coupon.token } }.

I think the best way would be to wrap the Vue.axios requests into your own Promise. Then, if the requests fail, you have the coupon tokens in your error.
Something like
const promises = [];
promises.push(
Vue.axios.post('/api_producer/coupons.json', coupon)
.catch(() => { throw new Error(coupon.token) }));
Promise.all(promises).catch(tokens => {
tokens.forEach(token => {
// Commit/handle errorous token
});
});

You can wrap your api call in another promise and check the status. Something like this:
promises.push(
new Promise((resolve, reject) => {
Vue.axios.post('/api_producer/coupons.json', coupon, { params: { token: coupon.token } })
.then((response) => {
if (response.status !== 200) {
coupon.error = true;
reject();
} else {
resolve();
}
});
})
);
The reject will keep these two lines from being executed:
dispatch('utilities/showModal', 'success', { root: true })
dispatch('fetchProducerCoupons')

Thanks to Moritz Schmitz v. Hülst & sklingler93 for the help, I restructured the code and it's working.
I'm wondering if there's a way to write all of this using only async/await... If anybody has an idea, would love to see it :)
saveCoupons({ state, rootState, dispatch, commit }) {
const promises = []
state.userCoupons.forEach(coupon => {
if (coupon.isNew && coupon.isUpdated) {
// if the user is creating a new coupon
promises.push(new Promise((resolve, reject) => {
Vue.axios.post('/api_producer/coupons.json', coupon)
.then(response => resolve(response))
.catch(err => {
reject(err)
commit('ADD_ERROR_ON_COUPON', coupon.token)
})
}))
} else if (!coupon.isNew && coupon.isUpdated) {
// if the user is updating the coupon
promises.push(new Promise((resolve, reject) => {
Vue.axios.patch(`api_producer/coupons/${coupon.id}/`, coupon)
.then(response => resolve(response))
.catch(err => {
reject(err)
commit('ADD_ERROR_ON_COUPON', coupon.token)
})
}))
}
})
Promise.all(promises)
.then(() => {
dispatch('utilities/showModal', 'success', { root: true })
dispatch('fetchProducerCoupons')
})
.catch(err => console.error(err))
},

Related

how to call second API after first API result is retrieved

I have a API which called the bank end :
this.http.get(`http://localhost:44301/consentinitiation/${this.qid}`)
.pipe(retryWhen(_ => {
this.showIt=true
return interval(1000)
}))
.subscribe(result => {result
console.log(result);
this.qrcodelink=result["qrCodeLink"];
setTimeout(() => {
this.loadingSpinner=false;
}, 5000);
})
It has a result and has a status which is "Recieved" after that i should call the API again unstil i get the status "Finalized" and dont how to call the API again ,after the first call is finished,because if i write it below the first one i guess they will call the simultaneously,any idea?
the problem that u describe in description is called polling ( make request in interval until u got an expected result )
here is poll implementation in rxjs way
makeSomeRequestsToBank() {
this.http.get('https://').pipe(
switchMap(result => {
// if status is recieved, start polling
if (result.status === 'Recieved') {
return this.pollStatus();
}
if (result.status === 'Finalized') {
return of(result)
}
// else do some thing else, depends on u business logic
// keep in mind that switchMap should return an observable for futher chaining
return of(undefined);
}),
).subscribe(result => {
if (!result) return;
this.qrcodelink=result["qrCodeLink"];
setTimeout(() => {
this.loadingSpinner=false;
}, 5000);
}
pollStatus(): Observable<any> {
const POLLING_INTERVAL = 700; // poll in ms
const isSuccessFn = (response: string) => response === 'Finalized'; // the condition to stop polling and return result
return timer(0, POLLING_INTERVAL).pipe(
switchMap(() => this.http.get('https://')),
skipWhile(response => isSuccessFn(response)),
);
}
You can do it with using Promise.
ngOnInit(){
this.callFirstApi();
}
firstApi(): Promise<any> {
return new Promise((resolve, reject) => {
this.http.get(API_URL).subscribe((data) => {
resolve(data);
}, (error) => {
reject(error);
});
});
}
secApi(): Promise<any> {
return new Promise((resolve, reject) => {
this.http.get(API_URL).subscribe((data) => {
resolve(data);
}, (error) => {
reject(error);
});
});
}
callFirstApi(){
this.firstApi().then(response => {
this.callSecApi();
}).catch(err => {
})
}
callSecApi(){
this.secApi().then(response => {
}).catch(err => {
})
}

How to make two api calls using Promise.all within Angular9?

I making an api call using Promise.all as below:
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
});
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
The above code is working fine.
Now I want to make another api call after the successful completion of this.serviceB.retry(oretry).
The second api is this.serviceB.createDbEntry(sentry) and sentry looks as below:
const sretry: SDInterface = {
hostName,
Id: this.Id.slice(0, this.Id.length),
reason: this.reason
};
And, I am doing it as below
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
const sretry: SDInterface = {
hostName,
Id: this.Id.slice(0, this.Id.length),
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
this.serviceB.createDbEntry(sentry).subscribe(resolve);
});
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
The above code is giving an error:
error: "SequelizeValidationError: string violation: Id cannot be an array or an object"
It is looks like it is not calling the second api for every Id
You may want to take a look a forkJoin
import { Observable, forkJoin } from 'rxjs';
And then
ngOnInit() {
let one = this.http.get('some/api/1') //some observable;
let two = this.http.get('some/api/2') // another observable;
forkJoin([one, tow]).subscribe(response => {
// results[0] is our one call
// results[1] is our second call
let var1 = response[1];
let var2 = response[0];
}/*, error => { in case error handler } */);
}
Wouldn't it be better to use Promise.all() once more?
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
});
})
.then(() => {
return Promise.all(this.Id.slice(0, this.Id.length).map(id => {
return new Promise((resolve, reject) => {
const sretry: SDInterface = {
hostName,
Id: id,
reason: this.reason
};
this.serviceB.createDbEntry(sentry).subscribe(resolve);
});
})
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
And using toPromise() will make the code more concise.
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
return this.serviceB.retry(oretry).toPromise();
})
.then(() => {
return Promise.all(this.Id.slice(0, this.Id.length).map(id => {
const sretry: SDInterface = {
hostName,
Id: id,
reason: this.reason
};
this.serviceB.createDbEntry(sentry).toPromise();
})
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
Use combineLatest, in Angular we use RxJs not promises.
combineLatest(
[this.http.get('call1'), this.http.get('call2')]
).subscribe(([result1, result2]) => {
// do stuff with result1 and result2
});
promise.all takes input in an array and gives response in an array,
Create 2 functions each with your asynchronous logic returning a promise,
Say funcA and funcB, then use below to invoke them parellely
Promise.all([funcA(this.hostName), funcB(this.id)])
.then(respones => {
console.log(responses[0]); //return value for funcA
console.log(responses[1]); //return value for funcB
})
.catch(err => console.log(err));
I am assuming your logic of functions are correct, I just copy-pasted from your question and gave them structure
const funcA = (hostName) => {
hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
});
});
});
}
const funcB = (Id) => {
Id.slice(0, this.Id.length).map(id => {
return new Promise((resolve, reject) => {
const sretry: SDInterface = {
hostName,
Id: id,
reason: this.reason
};
this.serviceB.createDbEntry(sentry).subscribe(resolve);
});
})
}

.then() fires before previous .then() has returned

I'm pulling category information from our local point of sale database (3rd party software) and trying to write it into a WooCommerce store. I'm also storing the data to my own database to maintain relationships between the two systems. I'm using promises to sequence everything, but one of my .then() statements is firing off before the previous .then() has returned, so I'm sending an empty payload to WooCommerce.
router.post("/:action", (req, res) => {
if(req.params.action === "sync" && req.body.action === "sync") {
// Query the POS database
mssql.query(query, (err, result) => {
let postData = {
create: [],
update: []
}
// Make some promises to pull the POS categories and their children
Promise.all(promises)
.then(cats => {
let catPromises = cats.map(cat => {
return new Promise((resolve, reject) => {
Category.findOne(
// Check for existing entry in the linking DB...
)
.then(data => {
// ...and handle accordingly
resolve()
})
.then(() => {
let childPromises = cat.children.map(child => {
return new Promise((resolve, reject) => {
Category.findOne(
// Checking for existing entry in the linking DB...
)
.then(data => {
// ...and handle accordingly
resolve()
})
})
})
Promise.all(childPromises)
.then(resolved => {
resolve()
})
})
})
})
Promise.all(catPromises)
.then(() => {
return
})
})
.then(() => {
// This is the part that's firing early
return axios.post(
// data
)
})
...
EDIT: Newly refactored, still having problems.
Promise.all(promises).then(cats => {
let catPromises = cats.map(cat => {
Category.findOne(
// Check for existing...
).then(data => {
// ...and handle accordingly
}).then(() => {
let childPromises = cat.children.map(child => {
Category.findOne(
// Check for existing...
).then(data => {
// ...and handle accordingly
})
})
return Promise.all(childPromises)
})
})
// Now this is where we're reaching early
return Promise.all(catPromises)
}).then(() => {
// API call
})
Final solution:
Promise.all(promises).then(cats => {
let catPromises = cats.map(cat => {
return Category.findOne(
// Check for existing...
).then(data => {
// ...and handle accordingly
}).then(() => {
let childPromises = cat.children.map(child => {
return Category.findOne(
// Check for existing...
).then(data => {
// ...and handle accordingly
})
})
return Promise.all(childPromises)
})
})
// Now this is where we're reaching early
return Promise.all(catPromises)
}).then(() => {
// API call
})

Why is this promise not resolving back to the caller?

I have a Vue-App which runs with Vuex and Axios. In this app I have vuex-store which handles API-calls, but a problem is that when I call the store-actions I cant chain the response in the caller.Any ideas what Im doing wrong?
Calling code:
import { FETCH_PRODUCTS, ADD_PRODUCT } from './actions.type'
methods: {
sendNewProduct () {
this.$store
.dispatch(ADD_PRODUCT, this.newProductForm)
.then(() => {
console.log('This never gets called')
})
}
}
Vuex-store:
const actions = {
[ADD_PRODUCT] (context, credentials) {
return new Promise((resolve) => {
ApiService
.post('/Products/', {
Name: credentials.Name,
Description: credentials.Description,
Price: credentials.Price
})
.then(({ data }) => {
this.$store
.dispatch(FETCH_PRODUCTS)
resolve(data)
})
.catch(({ response }) => {
console.log(response)
context.commit(SET_ERROR, 'Error adding product')
})
})
}
}
const actions = {
[ADD_PRODUCT](context, credentials) {
return ApiService.post("/Products/", {
Name: credentials.Name,
Description: credentials.Description,
Price: credentials.Price
})
.then(({ data }) => {
this.$store.dispatch(FETCH_PRODUCTS);
return data;
})
.catch(({ response }) => {
console.log(response);
context.commit(SET_ERROR, "Error adding product");
throw new Error("Error adding product");
});
}
};
I've removed the new Promise(...) because axios already creates a promise.
If added a return data in the then callback and a throw in the catch callback to let the calling api receive the data/error.
Note that the promise resolves before the FETCH_PRODUCTS completes, to make sure that action is also completed, you'd write:
.then(({ data }) => {
return this.$store.dispatch(FETCH_PRODUCTS)
.then(() => data);
})

JavaScript: Promise.all returning undefined

I'm trying to create a user account creation script with a focus on unique usernames - a prefix and a suffix from a pool, a list of existing usernames, and a list of reserved usernames.
That's just the start of it (no saving yet!), and already that would require three connections, so I just decided to see if I can code a function that would handle them all.
Here's my code so far - and it's on AWS Lambda, and tested via API Gateway, if that means anything:
const dbConnMysql = require('./dbController');
var methods = {
createUser: function() {
let getPrefixSuffixList = new Promise((resolve, reject) => {
let connection = dbConnMysql.createConnection();
dbConnMysql.startConnection(connection)
.then((fulfilled) => {
let table = 'userNamePool';
return dbConnMysql.selectFrom(connection, table, '*', null);
})
.then((fulfilled) => {
console.log(fulfilled);
return dbConnMysql.closeConnection(connection)
.then((fulfilled) => {
resolve(fulfilled);
});
})
.catch((error) => {
console.log(error);
reject(error);
});
});
let getTempUserNameList = new Promise((resolve, reject) => {
// Same as getPrefixSuffixList, different table
});
let getRealUserNameList = new Promise((resolve, reject) => {
// Same as getPrefixSuffixList, different table
});
return new Promise((resolve, reject) => {
Promise.all([getPrefixSuffixList, getTempUserNameList, getRealUserNameList])
.then((fulfilled) => {
console.log(fulfilled[0]);
console.log(fulfilled[1]);
console.log(fulfilled[2]);
let response = {
"statusCode": 200,
"headers": {"my_header": "my_value"},
"body": {"Prefix Suffix":fulfilled[0], "Temp UserName List":fulfilled[1], "Real UserName List":fulfilled[2]},
"isBase64Encoded": false
};
resolve(response);
})
.catch((error) => {
let response = {
"statusCode": 404,
"headers": {"my_header": "my_value"},
"body": JSON.stringify(error),
"isBase64Encoded": false
};
reject(response);
})
});
}
};
module.exports = methods;
This function is called elsewhere, from index.js:
app.get('/createUserName', function (req, res) {
var prom = Register.createUser();
prom.then((message) => {
res.status(201).json(message);
})
.catch((message) => {
res.status(400).json(message);
});
})
Now I'm not entirely sure if what I did with the Promise.All is correct, but from what little I know, if one promise fails, the Promise.All fails.
However, the individual promises do work just fine, and log out the respective results from the database. But inside the Promise.All, it all just logs out undefined.
Is there something I'm missing?
The cause of your problem is this. You need to run the functions, these then return the promise that will eventually resolve:
Promise.all([getPrefixSuffixList(), getTempUserNameList(), getRealUserNameList()])
Here is some simpler code as well. In general there is no need for new Promise(). This code may fix other issues. Also, the undefined could be being printed from any part of the code, make sure it's being printed where you think it is.
// Dummy MySQL connector
const dbConnMysql = {
createConnection: () => 'Connection',
startConnection: conn => new Promise(resolve => setTimeout(resolve, 100)),
selectFrom: (conn, t, q, n) =>
new Promise(resolve =>
setTimeout(() => {
console.log(`${conn}: SELECT ${q} FROM ${t}`);
resolve(`x ${t} RECORDS`);
}, 100)
),
closeConnection: conn => new Promise(resolve => setTimeout(resolve, 100)),
};
const methods = {
createUser() {
const getPrefixSuffixList = () => {
const connection = dbConnMysql.createConnection();
return dbConnMysql
.startConnection(connection)
.then(() => {
const table = 'userNamePool';
return dbConnMysql.selectFrom(connection, table, '*', null);
})
.then(fulfilled => {
console.log(fulfilled);
return dbConnMysql.closeConnection(connection).then(() => fulfilled);
})
.catch(error => {
console.log(error);
// Note: this catch will stop the error from propagating
// higher, it could also be the cause of your problem.
// It's okay to catch, but if you want the error to
// propagate further throw a new error here. Like this:
throw new Error(error);
});
};
const getTempUserNameList = () => {
// Same as getPrefixSuffixList, different table
};
const getRealUserNameList = () => {
// Same as getPrefixSuffixList, different table
};
return Promise.all([getPrefixSuffixList(), getTempUserNameList(), getRealUserNameList()])
.then(fulfilled => {
console.log('fulfilled[0]: ', fulfilled[0]);
console.log('fulfilled[1]: ', fulfilled[1]);
console.log('fulfilled[2]: ', fulfilled[2]);
return {
statusCode: 200,
headers: { my_header: 'my_value' },
body: {
'Prefix Suffix': fulfilled[0],
'Temp UserName List': fulfilled[1],
'Real UserName List': fulfilled[2],
},
isBase64Encoded: false,
};
})
.catch(error => ({
statusCode: 404,
headers: { my_header: 'my_value' },
body: JSON.stringify(error),
isBase64Encoded: false,
}));
},
};
methods.createUser();

Categories