1) I have a specific implementation where I want to adhere to the DRY principles.
The structure of both the methods is almost exactly the same. I am wondering if this is a way not to repeat the implementation:
addCardToExistingCustomer(cardDetail){
PaymentUtil.stripeCreateCardToken(cardDetail).then((cardTokenResult)=>{
if(cardTokenResult.error){
console.log("There was an error with the card!");
} else {
PaymentUtil.addCardToExistingCustomer(cardTokenResult.id).then((card) =>{
});
}
});
}
addCardToNewCustomer(cardDetail){
this.stripeCreateCardToken(cardDetail).then((cardTokenResult)=>{
if(cardTokenResult.error){
console.log("There was an error with the card!");
} else {
console.log("Successfully created card token");
PaymentUtil.stripeCreateCustomer(cardTokenResult.id)
}
});
}
2) Is this the best way to chain promises ? Specifically how should you handle exceptions from the promises in the chain which aren't the last element of the chain.
Say for example promise getStripeCustomerId rejected. How should you handle the the rejection.
addCardToExistingCustomer(cardTokenResultId){
return this.getStripeCustomerId(userDetail).then((customerId) => {
return this.removeAllExistingCards(userDetail).then(()=>{
return Stripe.addCardToCustomer(cardTokenResultId,customerId);
});
});
},
getStripeCustomerId(userDetail){
return FirebaseRESTUtil.getStripeCustomer(userDetail.username)
.then((fbStripe) => (fbStripe.customerId));
},
1- This way can be better.. It will use promises chains to work on data.
getCardTokenResult(cardDetail) {
return PaymentUtil.stripeCreateCardToken(cardDetail)
.then((cardTokenResult) => {
if(cardTokenResult.error){
return Promise.reject('There was an error with the card!!');
} else {
return cardTokenResult;
}
})
.catch((error) => console.log(error)):
}
addCardToExistingCustomer(cardDetail){
return getCardTokenResult(cardDetail)
.then((cardTokenResult) => {
PaymentUtil.addCardToExistingCustomer(cardTokenResult.id).then((card) =>{
// do something
});
});
}
addCardToNewCustomer(cardDetail){
return getCardTokenResult(cardDetail)
.then((cardTokenResult) => {
PaymentUtil.stripeCreateCustomer(cardTokenResult.id);
});
}
2- You can use catch to get errors on Promises chain. You can look the code above. I added catch to get cardtokenresult error. You can continue same way on chain to get other errors.
I noticed that you're checking for errors in the first argument of your then. I'm pretty sure this is incorrect as Stripe does not return HTTP 200 status on errors.
Having said that, I like to flatten chained promises instead of nesting them because I find that it makes the code easier to maintain and read:
addCardToExistingCustomer(cardTokenResultId) {
return this.getStripeCustomerId(userDetail).then((customerId) => {
return this.removeAllExistingCards(userDetail); // returns a promise
}).then(() => {
return Stripe.addCardToCustomer(cardTokenResultId,customerId); // returns a promise
});
},
Now any errors will bubble up to the main outer promise, so you can check for errors by
addCardToExistingCustomer(myId).then(() => {
console.log('success!');
}, (error) => {
console.log(error);
});
Related
This is my code snippet:
await firestore.runTransaction(t => {
t.get(docRef)
.then(docRef => {
logger.info("entering transaction");
if (!docRef.exists) {
t.create(docRef, new Data);
return Promise.resolve();
} else {
t.update(docRef, {aField: updateData});
return Promise.resolve();
}
})
.catch(error => {
return Promise.reject(error);
});
})
This gives me the following error:
Error: You must return a Promise in your transaction()-callback. at transaction.begin.then (/srv/node_modules/#google-cloud/firestore/build/src/index.js:496:32) at
It's my first time using firestore transaction syntax, and I know fairly about nodejs promise concepts and syntax. I mimiced the firestore transaction example here and wrote the above snippet.
After the error, I tried to change Promise.resolve() to Promise.resolve(0), but in vain.
I also tried to remove await and instead use .then, but it still gives me the same error.
Please shed some lights on it. Appreciate it. Thanks.
You need to basically return the Promise. Also, if t.create and t.update returns a promise as well, you can just return that. Don't do return Promise.resolve(), Which is wrong because it will resolve BEFORE actually inserting or creating.
Assuming everything works fine you need to do:
await firestore.runTransaction(t => {
return t.get(docRef)
.then(docRef => {
logger.info("entering transaction");
if (!docRef.exists) {
return t.create(docRef, new Data);
} else {
return t.update(docRef, { aField: updateData });
}
})
.catch(error => {
return Promise.reject(error);
});
})
Looking back the example, I realized i missed return in return t.get() but the error was in no way obvious to point out my mistake..
I have the following code that uses callbacks inside promises:
const clue = 'someValue';
const myFunction = (someParam, callback) => {
someAsyncOperation(someParam) // this function returns an array
.then((array) => {
if (array.includes(clue)){
callback(null, array); // Callback with 'Success'
}
else{
callback(`The array does not includes: ${clue}`); // Callback with Error
}
})
.catch((err) => {
// handle error
callback(`Some error inside the promise chain: ${err}`) // Callback with Error
})
}
and call it like this:
myFunction (someParam, (error, response) => {
if(error) {
console.log(error);
}
else {
// do something with the 'response'
}
})
Reading some documentation, I found that there is some improved way to do this:
const myFunction = (someParam, callback) => {
someAsyncOperation(someParam) // this function returns an array
.then((array) => {
if (array.includes(clue)){
callback(array);
}
else{
callback(`The array does not includes: ${clue}`);
}
}, (e) => {
callback(`Some error happened inside the promise chain: ${e}`);
})
.catch((err) => {
// handle error
callback(`Some error happened with callbacks: ${err}`)
})
}
My question:
In the sense of performance or best practices, it's okay to call the 'callback' function inside the promise as the two ways show or I'm doing something wrong, I mean some promise anti-pattern way ?
This seems really backwards and takes away from the benefits of promises managing errors and passing them down the chain
Return the asynchronous promise from the function and don't interrupt it with callbacks. Then add a catch at the end of the chain
const myFunction = (someParam) => {
// return the promise
return someAsyncOperation(someParam) // this function returns an array
.then((array) => {
return array.includes(clue) ? array : [];
});
}
myFunction(someParam).then(res=>{
if(res.length){
// do something with array
}else{
// no results
}
}).catch(err=>console.log('Something went wrong in chain above this'))
Do not use callbacks from inside of promises, that is an anti-pattern. Once you already have promises, just use them. Don't "unpromisify" to turn them into callbacks - that's moving backwards in code structure. Instead, just return the promise and you can then use .then() handlers to set what you want the resolved value to be or throw an error to set what you want the rejected reason to be:
const clue = 'someValue';
const myFunction = (someParam) => {
return someAsyncOperation(someParam).then(array => {
if (!array.includes(clue)){
// reject promise
throw new Error(`The array does not include: ${clue}`);
}
return array;
});
}
Then, the caller would just do this:
myFunction(someData).then(array => {
// success
console.log(array);
}).catch(err => {
// handle error here which could be either your custom error
// or an error from someAsyncOperation()
console.log(err);
});
This gives you the advantage that the caller can use all the power of promises to synchronize this async operation with any others, to easily propagate errors all to one error handler, to use await with it, etc...
I want to be able to reject the entire promise chain if anyone of the promise fails in a clean way. I want to "catch" this rejection and send an error notification. I have implemented it the following code:
let reportMetaData = api.ajaxGet(api.buildV3EnterpriseUrl('reports' + '/' + params.report_id))
.catch(error => {
if (error.status === constants.HTTP_STATUS.GATEWAY_TIMEOUT) {
this.notify.error(this.translate('reports.report_timedout'), this.translate('reports.report_timedout_desc'));
} else {
this.send('error', error);
}
});
let aggregateData = reportMetaData.then(success => {
try {
return api.xmlRequest('GET', success.aggregationUrls.elements[0].url);
} catch (error) {
return Promise.reject();
}
}).then(rawData => {
try {
return JSON.parse('{' + rawData + '}');
} catch (error) {
return Promise.reject();
}
}, error => Promise.reject(error));
let aggregateReport = aggregateData.then(data => {
if (!data || !data.report) {
return Promise.reject();
}
return data.report;
}).catch(error =>{
this.notify.error(this.translate('reports.report_timedout'), error);
});
As you can see, it is super messy and complicated. Is there a way I can simplify this? I want the simplest way to reject the entire promise to fail if anyone promise fails. How do I do that from inside the then function? Also, it seems like the thrown error is bubbling up all the way to chrome console as uncaught error. Why does it bubble up even though I caught it?
You need to use Promise.all() and provide the array of promises as input parameter.
If one of those promises will fail, all the promises will not be resolved.
Here the doc:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
And here a post on SO where you can read about them:
When to use promise.all()?
Try aggregating everything under Promise.all(iterable).
More here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
If this is not quite what you wanted, take a look at Bluebird - fully featured promise library. Here
UPDATE: If you want to reject the entire promise if any of the promises inside the function fails, try implementing:
throw validationError;
Hope it works.
You could use async functions to clean things up a bit. I think you could replace your code with the following.
async function processDataAndReport() {
try {
const data = await api.ajaxGet(api.buildV3EnterpriseUrl('reports' + '/' + params.report_id));
const rawData = await api.xmlRequest('GET', data.aggregationUrls.elements[0].url);
const { report } = JSON.parse(`{${rawData}}`);
} catch(e) {
// send notification
}
}
I am wondering how can I properly break promise chain in JS.
In this code, I am connecting to database at first, then I am checking if collection already has some data, if not add them. Don't pay attention to some actionhero.js code..it does not matter here.
The main question is: Is it okay to break chain using throw null?
mongoose.connect(api.config.mongo.connectionURL, {})
.then(() => {
return api.mongo.City.count();
})
.then(count => {
if (count !== 0) {
console.log(`Amout of cities is ${count}`);
throw null; // break from chain method. Is it okay ?
}
return api.mongo.City.addCities(api.config.mongo.dataPath + '/cities.json');
})
.then(cities => {
console.log("Cities has been added");
console.log(cities);
next();
})
.catch(err => {
next(err);
})
Many thanks!
Despite it may seem like a clever trick and will work as You expect, I would advise against throwing non-error objects.
It would be much more predictable for other developers that will maintain this code if You throw an actual Error and handle it explicitly.
Promise
.resolve()
.then(() => {
throw new Error('Already in database');
})
.catch(err => {
if (err.message === 'Already in database') {
// do nothing
} else {
next(err);
}
});
Here is what my code looks like:
return request.app.currentProject().then(function(project) {
return project.prs().query({
where: {id: request.params.prId}
}).fetchOne().then(function(pr) {
if (!pr) { return reply(Boom.notFound()); }
pr.destroy().then(function(model) {
return reply().code(204);
}).catch(function(err) {
return reply(Boom.badRequest(err));
});
}).catch(function(err) {
return reply(Boom.badRequest(err));
});
}).catch(function(err) {
return reply(Boom.badRequest(err));
});
Right now, in the code above, I have to catch each promise individually. Is there anyway to chain them so the errors bubble up without having to catch each promise individually?
Update
return request.app.currentProject().then(function(project) {
return project.prs().query({
where: {id: request.params.prId}
}).fetchOne();
}).then(function(pr) {
return pr.destroy();
}).catch(function(err) {
return reply(Boom.badRequest(err));
});
Promise errors bubble up automatically; you don't need to do anything.
Just remove all error handlers except at the end of the chain.
For more detailed guidelines for error handling, see my blog.