Reputation: 7344
Let's suppose I have a Parse Cloud Code js function which I want to return a promise, like:
function doSomething(myObj, abortIfSaveFails) {
var dsPromise = new Parse.Promise();
myObj.set("name", "abc");
myObj.save().then(function(){
// location "A"
// great, it worked!
// don't want to actually do anything more in this block because
// we might want to do the same things even if save fails, and I
// don't want to repeat code
return Parse.Promise.as();
}, function(error){
// location "B"
// save failed, but we might want to keep going
if (abortIfSaveFails) {
// location "C": wish I could abort the whole promise chain here!
return Parse.Promise.error();
} else {
return Parse.Promise.as();
}
}).then(function(){
// location "D"
// at this point we're not sure if save succeeded but let's
// assume we don't need to know
return doSomethingCruciallyImportantAndReturnAPromise();
}, function(error){
// location "E":
// not sure if we got here because doSomethingCruciallyImportant...() errored or
// because myObj.save() errored.
// would be nice to abort the whole thing right now!
return Parse.Promise.error();
}).then(function(){
// location "F"
// at this point we know doSomethingElse... succeeded
return doSomethingUnimportantAndReturnAPromise();
}, function(error){
// location "G"
// not sure if we got here because doSomethingCruciallyImportant...() errored or
// because doSomethingUnimportant...() errored.
// If doSomethingCruciallyImportant...() succeeded but doSomethingUnimportant...()
// failed, I'd LIKE to do dsPromise.resolve()... but I can't resolve, because
// we might be in the process of aborting the function because myObj.save() rejected,
// or doSomethingCruciallyImportant rejected!
dsPromise.reject(); // might not be what I want to do!
}).then(function(){
// location "H"
// everything worked fine
dsPromise.resolve();
});
// location "I"
return dsPromise; // return the promise so `then` will wait
}
How can I refactor/rewrite this to better handle the situations at locations C, E, and G?
I realize I could dsPromise.reject() at C and E, but what would happen to the currently executing promise chain? Wouldn't it keep executing and move on to D, E, F, G etc.? Then couldn't I get to a place where I'm resolving dsPromise multiple times?
Upvotes: 1
Views: 2092
Reputation: 664297
How can I refactor/rewrite this to better handle the situations at locations C, E, and G?
Nest handlers appropriately. If a handler is supposed to handle the resolution of only a single action, then chain it with .then
directly on the promise for that action, and not somewhere else in the chain.
In location E, you would not attach a handler to the chain that includes the save call, but only to promises in the onFulfilled
(i.e. not aborted) branch.
doSomethingCruciallyImportant().then(function(){ // location "F" // at this point we know doSomethingCruciallyImportant succeeded return doSomethingUnimportant(); }, function(error){ // location "G" // not sure if we got here because doSomethingCruciallyImportant() errored // or because doSomethingUnimportant() errored. });
No. Read more about how the onRejection
handler of .then()
actually works - it is not called when the onFulfilled
handler is executed (which calls doSomethingUnimportant()
here). In location G, you know for sure that something in the chain before the then
call failed - doSomethingCruciallyImportant()
in my simplified snippet.
In combination:
function doSomething(myObj, abortIfSaveFails) {
myObj.set("name", "abc");
return myObj.save().then(null, // no onFulfilled block at all!
function(error){
if (abortIfSaveFails) {
return Parse.Promise.error(); // abort
} else {
return Parse.Promise.as(); // keep going regardless of the save fail
}
}).then(function() {
// either save succeeded or we don't care about it
return doSomethingCruciallyImportantAndReturnAPromise()
.then(function(){
// location "F"
// at this point we know doSomethingCruciallyImportant succeeded
return doSomethingUnimportantAndReturnAPromise().then(null, function(err) {
return Parse.Promise.as(); // we don't care if it errored
});
} //, function(error){
// location "G"
// doSomethingCruciallyImportant...() errored
// return Parse.Promise.error();
// }
);
}).then(function(result) {
// location "H"
// everything worked fine: Save succeeded (or we didn't care) and
// doSomethigCruciallyImportant() did as well
return result;
});
}
Upvotes: 4