Recovering from rejected promises in JS - javascript

I'm using native promises (mostly) and attempting to recover from an error and continue executing the promise chain.
Effectively, I'm doing this:
REST query to see if ID exists. Note that this returns a jquery deferred.
.then (success means ID exists, so fail and stop)
(fail means ID does not exist, so continue creating ID)
.then (create the ID record and send to the server)
I return a Promise.resolve() from my rejected function, which should cause the success part of the next .then to execute. It does not. I've tried this on Chrome and Safari.
Note that the first promise is actually a query deferred, but according to this page (http://api.jquery.com/deferred.then/), deferred.then() returns a promise object. So adding an extra .then should covert to native promises.
To make it clearer - here's the pseudocode:
promise = $.ajax(url);
promise = promise.then(); // convert to promise
promise.then(function() { cleanup(); return Promise.reject(); },
function(err) { return Promise.resolve(); });
.then(function() { createIdentityDetails(); });
.then(function() { sendIdentityDetails(); });
Note that I want to FAIL when the ajax returns success, and I want to
continue processing when the ajax call fails.
What happens is that the FAIL functions for all subsequent .then portions execute. That is, my return Promise.resolve() doesn't work - which is (I think) in violation of the spec.
I'd appreciate any feedback on how I can deal with and recover from errors in long promise chains.
Many thanks for any advice you can provide.
p.s. creating and collecting the full identity information is quite time consuming, so I don't want to do it if the ID exists. Hence I want to check first and fail quickly.
p.p.s I really like the way that promises have unwound these deeply nested async callback chains.

Assuming createIdentityDetails() and sendIdentityDetails() to be promise-returning asynchronous functions ...
If what we see in the question is the entirety of the promise chain, then handling the error condition is simple. It's not necessary to convert success to failure or failure to success, or from one type of promise to another.
$.ajax(url).then(function() {
cleanup();
}, function(err) {
createIdentityDetails()
.then(sendIdentityDetails);
});
This will work regardless of the type of promise returned by createIdentityDetails() jQuery or non-jQuery.
If, however, there's more to it, eg a caller function needs to be informed of the outcome, then you need to do more, and it depends on how you want the possible outcomes to be reported.
Report 'ID already exists' as failure and 'new ID created' as success
This is what the question suggests
function foo() {
return $.ajax(url).then(function() {
cleanup();
return $.Deferred().reject('failure: ID already exists');
}, function(err) {
return createIdentityDetails()
.then(sendIdentityDetails)
.then(function() {
return $.when('success: new ID created');
});
});
}
Report both types of outcome as success
This seems more sensible as the handled error will be reported as success. Only unpredicted, unhandled errors will be reported as such.
function foo() {
return $.ajax(url).then(function() {
cleanup();
return 'success: ID already exists';
}, function(err) {
return createIdentityDetails()
.then(sendIdentityDetails)
.then(function() {
return $.when('success: new ID created');
});
});
}
Whichever reporting strategy is adopted, it matters very much what type of promise createIdentityDetails() returns. As the first promise in the chain it determines the behaviour of both its chained .thens.
if createIdentityDetails() returns a native ES6 promise, then no worries, most flavours of promise, even jQuery, will be assimilated.
if createIdentityDetails() returns a jQuery promise, then only jQuery promises will be assimilated. Therefore sendIdentityDetails() must also return a jQuery promise (or an ES6 promise which must be recast into jQuery with $.Deferred(...)), as must the final success converter (as coded above).
You can see the effects of mixing jQuery and ES6 promises in these two ways here. The first alert is generated by the second block of code, and is not what is expected. The second alert is generated by the first block and correctly gives the result 98 + 1 + 1 = 100.

promise = promise.then(); // convert to promise
Huh? A promise returned by $.ajax is already a promise.
promise.then(function() { cleanup(); return Promise.reject(); },
function(err) { return Promise.resolve(); });
The problem with this is that jQuery is not Promises/A+ compatible, and fails to adopt promises/thenable from other implementations than its own. You would have to use $.Deferred here to make this work, like
promise.then(function() { cleanup(); return $.Deferred().reject(); },
function() { return $.when(); }); // or return $.Deferred().resolve();
That is, my return Promise.resolve() doesn't work - which is (I think) in violation of the spec.
Indeed it is. However, jQuery is known for this, and they won't fix it until v3.0.
To get the native Promise library you want to use working, you will need to avoid jQuery's then. This can easily be done:
var $promise = $.ajax(url);
var promise = Promise.resolve($promise); // convert to proper promise
promise.then(function() {
cleanup();
throw undefined;
}, function(err) {
return undefined;
})
.then(createIdentityDetails)
.then(sendIdentityDetails);

It seems that JQuery promises do not permit you to change a failure to a success. If, however, you use native promises, you can.
For example:
Promise.resolve()
.then(function() {console.log("First success"); return Promise.reject(); },
function() { console.log("First fail"); return Promise.resolve(); })
.then(function() {console.log("Second success"); return Promise.reject(); },
function() { console.log("Second fail"); return Promise.resolve(); })
.then(function() {console.log("Third success"); return Promise.reject(); },
function() { console.log("Third fail"); return Promise.resolve(); })
Here I return a reject from the first success handler. In the second failure handler I return a resolve. This all works as expected. The output is (Chrome):
First success
Second fail
Third success
It turns out the proper way to deal with jQuery deferreds and promises is to cast them:
var jsPromise = Promise.resolve($.ajax('/whatever.json'));
(from http://www.html5rocks.com/en/tutorials/es6/promises/).
This works nicely, so if you change the initial line above to:
Promise.resolve($.ajax("this will fail"))
...
you correctly get:
First fail
Second success
Third fail
Bottom line... cast deferred to promise asap, then everything seems to work right.

Hopefully this will clear things up a bit, you had a couple of stray ; and you're doing things you don't really need to do in the then functions
firstly, I'm sure you DO NOT want the
promise = promise.then();
line, the code would look like this
promise = $.ajax(url);
promise.then(function() {
cleanup();
throw 'success is an error'; // this is equivalent to return Promise.reject('success is an error');
}, function(err) {
return 'failure is good'; // returning here means you've nullified the rejection
}) // remove the ; you had on this line
.then(function() { createIdentityDetails(); }) // remove the ; on this line
.then(function() { sendIdentityDetails(); }) // remove the ; on this line
.catch(function(err) { }); // you want to catch the error thrown by success

Related

returning error from promise in angularjs [duplicate]

I am have a problem understanding why rejections are not passed on through a promise chain and I am hoping someone will be able to help me understand why. To me, attaching functionality to a chain of promises implies an intent that I am depending on an original promise to be fulfilled. It's hard to explain, so let me show a code example of my problem first. (Note: this example is using Node and the deferred node module. I tested this with Dojo 1.8.3 and had the same results)
var d = require("deferred");
var d1 = d();
var promise1 = d1.promise.then(
function(wins) { console.log('promise1 resolved'); return wins;},
function(err) { console.log('promise1 rejected'); return err;});
var promise2 = promise1.then(
function(wins) { console.log('promise2 resolved'); return wins;},
function(err) { console.log('promise2 rejected'); return err;});
var promise3 = promise2.then(
function(wins) { console.log('promise3 resolved'); return wins;},
function(err) { console.log('promise3 rejected'); return err;});
d1.reject(new Error());
The results of running this operation is this output:
promise1 rejected
promise2 resolved
promise3 resolved
Okay, to me, this result doesn't make sense. By attaching to this promise chain, each then is implying the intent that it will be dependant upon the successful resolution of d1 and a result being passed down the chain. If the promise in promise1 doesn't receive the wins value, but instead gets an err value in its error handler, how is it possible for the next promise in the chain to have its success function called? There is no way it can pass on a meaningful value to the next promise because it didn't get a value itself.
A different way I can describe what I'm thinking is: There are three people, John, Ginger, and Bob. John owns a widget shop. Ginger comes into his shop and requests a bag of widgets of assorted colours. He doesn't have them in stock, so he sends in a request to his distributor to get them shipped to him. In the mean time, he gives Ginger a rain check stating he owes her the bag of widgets. Bob finds out Ginger is getting the widgets and requests that he get the blue widget when she's done with them. She agrees and gives him a note stating she will. Now, John's distributor can't find any widgets in their supply and the manufacturer doesn't make them any more, so they inform John, who in turn informs Ginger she can't get the widgets. How is Bob able to get a blue widget from Ginger when didn't get any herself?
A third more realistic perspective I have on this issue is this. Say I have two values I want updated to a database. One is dependant on the id of the other, but I can't get the id until I have already inserted it into a database and obtained the result. On top of that, the first insert is dependant on a query from the database. The database calls return promises that I use to chain the two calls into a sequence.
var promise = db.query({parent_id: value});
promise.then(function(query_result) {
var first_value = {
parent_id: query_result[0].parent_id
}
var promise = db.put(first_value);
promise.then(function(first_value_result) {
var second_value = {
reference_to_first_value_id: first_value_result.id
}
var promise = db.put(second_value);
promise.then(function(second_value_result) {
values_successfully_entered();
}, function(err) { return err });
}, function(err) { return err });
}, function(err) { return err });
Now, in this situation, if the db.query failed, it would call the err function of the first then. But then it would call the success function of the next promise. While that promise is expecting the results of the first value, it would instead get the error message from its error handler function.
So, my question is, why would I have an error handing function if I have to test for errors in my success function?
Sorry for the length of this. I just didn't know how to explain it another way.
UPDATE and correction
(Note: I removed a response I had once made to some comments. So if anyone commented on my response, their comments might seem out of context now that I removed it. Sorry for this, I am trying to keep this as short as possible.)
Thank you everybody who replied. I would like to first apologize to everybody for writing out my question so poorly, especially my pseudo code. I was a little too aggressive in trying to keep it short.
Thanks to Bergi's response, I think I found the error in my logic. I think I might have overlooked another issue that was causing the problem I was having. This is possibly causing the promise chain work differently than I thought it should. I am still testing different elements of my code, so I can't even form a proper question to see what I'm doing wrong yet. I did want to update you all though and thank you for your help.
To me, this result doesn't make sense. By attaching to this promise chain, each then is implying the intent that it will be dependant upon the successful resolution of d1 and a result being passed down the chain
No. What you are describing is not a chain, but just attaching all the callbacks to d1. Yet, if you want to chain something with then, the result for promise2 is dependent on the resolution of promise1 and how the then callbacks handled it.
The docs state:
Returns a new promise for the result of the callback(s).
The .then method is usually looked upon in terms of the Promises/A specification (or the even stricter Promsises/A+ one). That means the callbacks shell return promises which will be assimilated to become the resolution of promise2, and if there is no success/error handler the respective result will in case be passed directly to promise2 - so you can simply omit the handler to propagate the error.
Yet, if the error is handled, the resulting promise2 is seen as fixed and will be fulfilled with that value. If you don't want that, you would have to re-throw the error, just like in a try-catch clause. Alternatively you can return a (to-be-)rejected promise from the handler. Not sure what Dojo way to reject is, but:
var d1 = d();
var promise1 = d1.promise.then(
function(wins) { console.log('promise1 resolved'); return wins;},
function(err) { console.log('promise1 rejected'); throw err;});
var promise2 = promise1.then(
function(wins) { console.log('promise2 resolved'); return wins;},
function(err) { console.log('promise2 rejected'); throw err;});
var promise3 = promise2.then(
function(wins) { console.log('promise3 resolved'); return wins;},
function(err) { console.log('promise3 rejected'); throw err;});
d1.reject(new Error());
How is Bob able to get a blue widget from Ginger when didn't get any herself?
He should not be able. If there are no error handlers, he will just perceive the message (((from the distributor) from John) from Ginger) that there are no widgets left. Yet, if Ginger sets up an error handler for that case, she still might fulfill her promise to give Bob a widget by giving him a green one from her own shack if there are no blue ones left at John or his distributor.
To translate your error callbacks into the metapher, return err from the handler would just be like saying "if there are no widgets left, just give him the note that there are no ones left - it's as good as the desired widget".
In the database situation, if the db.query failed, it would call the err function of the first then
…which would mean that the error is handled there. If you don't do that, just omit the error callback. Btw, your success callbacks don't return the promises they are creating, so they seem to be quite useless. Correct would be:
var promise = db.query({parent_id: value});
promise.then(function(query_result) {
var first_value = {
parent_id: query_result[0].parent_id
}
var promise = db.put(first_value);
return promise.then(function(first_value_result) {
var second_value = {
reference_to_first_value_id: first_value_result.id
}
var promise = db.put(second_value);
return promise.then(function(second_value_result) {
return values_successfully_entered();
});
});
});
or, since you don't need the closures to access result values from previous callbacks, even:
db.query({parent_id: value}).then(function(query_result) {
return db.put({
parent_id: query_result[0].parent_id
});
}).then(function(first_value_result) {
return db.put({
reference_to_first_value_id: first_value_result.id
});
}.then(values_successfully_entered);
#Jordan firstly as commenters noted, when using deferred lib, your first example definitely produces result you expect:
promise1 rejected
promise2 rejected
promise3 rejected
Secondly, even if it would produce output you suggest, it wouldn't affect execution flow of your second snippet, which is a bit different, more like:
promise.then(function(first_value) {
console.log('promise1 resolved');
var promise = db.put(first_value);
promise.then(function (second_value) {
console.log('promise2 resolved');
var promise = db.put(second_value);
promise.then(
function (wins) { console.log('promise3 resolved'); },
function (err) { console.log('promise3 rejected'); return err; });
}, function (err) { console.log('promise2 rejected'); return err;});
}, function (err) { console.log('promise1 rejected'); return err});
and that, in case of first promise being rejected will just output:
promise1 rejected
However (getting to the most interesting part) even though deferred library definitely returns 3 x rejected, most of other promise libraries will return 1 x rejected, 2 x resolved (that leads to assumption you got those results by using some other promise library instead).
What's additionally confusing, those other libraries are more correct with their behavior. Let me explain.
In a sync world counterpart of "promise rejection" is throw. So semantically, async deferred.reject(new Error()) in sync equals to throw new Error().
In your example you're not throwing errors in your sync callbacks, you just returning them, therefore you switch to success flow, with an error being a success value. To make sure rejection is passed further, you need to re-throw your errors:
function (err) { console.log('promise1 rejected'); throw err; });
So now question is, why do deferred library took returned error as rejection?
Reason for that, is that rejection in deferred works a bit different. In deferred lib the rule is: promise is rejected when it's resolved with an instance of error, so even if you do deferred.resolve(new Error()) it will act as deferred.reject(new Error()), and if you try to do deferred.reject(notAnError) it will throw an exception saying, that promise can be rejected only with instance of error. That makes clear why error returned from then callback rejects the promise.
There is some valid reasoning behind deferred logic, but still it's not on par with how throw works in JavaScript, and due to that this behavior is scheduled for change with version v0.7 of deferred.
Short summary:
To avoid confusion and unexpected results just follow the good practice rules:
Always reject your promises with an error instances (follow rules of sync world, where throwing value that's not an error is considered a bad practice).
Reject from sync callbacks by throwing errors (returning them doesn't guarantee rejection).
Obeying to above, you'll get both consistent and expected results in both deferred and other popular promise libraries.
Use can wrap the errors at each level of the Promise. I chained the errors in TraceError:
class TraceError extends Error {
constructor(message, ...causes) {
super(message);
const stack = Object.getOwnPropertyDescriptor(this, 'stack');
Object.defineProperty(this, 'stack', {
get: () => {
const stacktrace = stack.get.call(this);
let causeStacktrace = '';
for (const cause of causes) {
if (cause.sourceStack) { // trigger lookup
causeStacktrace += `\n${cause.sourceStack}`;
} else if (cause instanceof Error) {
causeStacktrace += `\n${cause.stack}`;
} else {
try {
const json = JSON.stringify(cause, null, 2);
causeStacktrace += `\n${json.split('\n').join('\n ')}`;
} catch (e) {
causeStacktrace += `\n${cause}`;
// ignore
}
}
}
causeStacktrace = causeStacktrace.split('\n').join('\n ');
return stacktrace + causeStacktrace;
}
});
// access first error
Object.defineProperty(this, 'cause', {value: () => causes[0], enumerable: false, writable: false});
// untested; access cause stack with error.causes()
Object.defineProperty(this, 'causes', {value: () => causes, enumerable: false, writable: false});
}
}
Usage
throw new TraceError('Could not set status', srcError, ...otherErrors);
Output
Functions
TraceError#cause - first error
TraceError#causes - list of chained errors
a simple explanation from here:
In a regular try..catch we can analyze the error and maybe rethrow it if it can’t be handled. The same thing is possible for promises.
If we throw inside .catch, then the control goes to the next closest error handler. But if we handle the error and finish normally, then it continues to the next closest successful .then handler.
In the example below the .catch successfully handles the error:
new Promise((resolve, reject) => {
throw new Error("Whoops!");
}).catch(function(error) {
alert("The error is handled, continue normally");
}).then(() => alert("Next successful handler runs"));
Here the catch block finishes normally. So the next successful then handler is called.
note that we may have as many .then handlers as we want, and then use a single .catch at the end to handle errors in all of them.
If you have mid catch blocks and you want to break the next chain functions for errors, you shall re-throw the errors inside the catch blocks to signal this error is not handled completely.
new Promise((resolve, reject) => {
throw new Error("Whoops!");
}).catch(function(error) { // (*) first catch
if (error instanceof URIError) { //just as example
// handle it...
} else {
alert("Can't handle such error");
throw error; // throwing this jumps to the next catch
}
}).then(function() {
// our error is other than URIError, so:
// the code doesn't reach here (jump to next catch)
}).catch(error => { // (**) second catch
alert(`The unknown error has occurred: ${error}`);
// don't return anything => execution goes the normal way
});
In the above example we see the first catch (*) will catch the error but can’t handle it (e.g. it only knows how to handle URIError), so it throws it again. The execution jumps from the first catch (*) to the next one (**) down the chain.

AngularJs pass promise response as argument to function

I ran into a small dilemma. In this angularJs app (1.3.7v) I fetch requirements as well as user belongings from the server and perform some front-end validation. Which looks something like this:
validationFunction = function(userId){
return new Promise(function(resolve, reject){
Promise.all([
requirementService.requirementApi.requirements.getAll().$promise,
userBelongingsService.userBelongingsApi.getAll(userId).$promise
]).then(function(requirements,belongings){
console.log(requirements, belongings);
resolve(isValid(requirements, belongings));
}).catch(function (err) {
reject(err);
});
});
}
For reference I also tried
validationFunction = function(userId){
return new Promise(function(resolve, reject){
requirementService.requirementApi.requirements.getAll().$promise.then(function(requirements) {
return [requirements, userBelongingsService.userBelongingsApi.getAll(userId)];
}).then(function(requirements,belongings){
console.log(requirements, belongings);
resolve(isValid(requirements, belongings));
}).catch(function (err) {
reject(err);
});
});
}
The promises are async and so they only get resolved later. But if used with .then() it should wait until they are resolved (rejected or resolved). Although in both methods the promises are resolve(at some time) at the moment when they are passed to the function they are not. And so the function fails saying that they are undefined. So what is the other way or am I missing something? :)
your code can be simplified to
validationFunction = function(userId){
return Promise.all([
requirementService.requirementApi.requirements.getAll().$promise,
userBelongingsService.userBelongingsApi.getAll(userId).$promise
]).then(function([requirements,belongings]){
console.log(requirements, belongings);
return isValid(requirements, belongings);
});
};
However, that contains some es2015+ specifics
so, in less modern JS
validationFunction = function(userId){
return Promise.all([
requirementService.requirementApi.requirements.getAll().$promise,
userBelongingsService.userBelongingsApi.getAll(userId).$promise
]).then(function(results){
console.log(results[0], results[1]);
return isValid(results[0], results[1]);
});
};
edit: sorry, I missed a ) in the above code originally
To expand on the answer a little (got distracted with the real world) - Firstly, as #Bergi pointed out, Avoid the Promise constructor antipattern! - as you are dealing with functions that return a promise (Promise.all, ....getAll().$promise, ....getAll(userId).$promise) there's no need to wrap all that in a new Promise
Secondly, the onfulfilled callback of .then is called with a single argument, the resolved value. In the case of Promise.all, this is an array of resovled values, in the same order as they appear in the Promise.all argument. That's why the ES2015+ version can use the ([a, b]) form for the function argument (sorry, I can't recall or find the "name" for this form). It's important to note that if either one of those Promises in the Promise.all array are rejected, Promise.all rejects with the rejected value of the (first) rejected promise (that's a lot of rejection in that sentence!)
You can also remove the .catch portion of your code, as the error handling can (and should) be done by the calling code to your function
validationFunction(someUserId)
.then(function(result) {
// this will be the value of isValid(requirements, belongings)
})
.catch(function(err) {
// do some error handling here
});
and finally, in it's most ES2015+ form (that I can think of)
validationFunction = userId => Promise.all([
requirementService.requirementApi.requirements.getAll().$promise,
userBelongingsService.userBelongingsApi.getAll(userId).$promise
]).then(([requirements, belongings]) => isValid(requirements, belongings));

Promises: is .done() executed always even if .catch() is?

My Promise issue
I am new to Promises and I've been reading the Q Documentation, where it says:
When you get to the end of a chain of promises, you should either return the last promise or end the chain.
I have defined a Promise in my code the Q.Promise way, with the following console.logs to log out an execution trace:
function foo(){
return Q.Promise(function(resolve, reject) {
doSomething()
.then(function() {
console.log('1');
return doSomething1();
})
.then(function() {
console.log('2');
return doSomething2();
})
.then(function() {
console.log('3');
return doSomething3();
})
.catch(function(err) {
console.log('catch!!');
reject(err);
})
.done(function() {
console.log('done!!');
resolve();
});
});
}
In case every doSomethingN() executes correctly, everything works as intended and I get the expected trace:
1
2
3
done!!
But in case any of the doSomethingN() fails:
foo() works correctly, because the error function callback is the one that runs whenever a reject(err) occurs:
foo().then(function() { /* */ }, function(err) { /* this runs! */ });
And I get the following trace (ie. when doSomething1() fails):
1
catch!!
done!!
My question
What I thought at first was the following:
Okay, let's handle the chaining success and failure in both: .done() and .catch() methods. If everything goes well .done()'s callback will be executed and the promise will be resolved. In case there's an error at any point, .catch()'s callback will be executed and the promise will be rejected - and because of that, done() won't be executed.
I think I am missing something about how the .done() works... because by having a look at my logging trace, I realized that .done() seems to be executing always - whether there is an error and .catch() is executed or not - and that is what I wasn't expecting.
So, after that, I removed .done()'s callback and now foo():
works if there's an error during the chain execution
does not work if everything works correctly
What should I reconsider and how could/should I make it work?
catch(cb) is just an alias for then(null, cb), and you've actually fixed an error in catch, so flow naturally turned to success result in done.
If you want to just decorate the error in catch, you should rethrow the error afterwards, e.g. proper passthru may look as:
catch(function (err) {
console.log(err);
throw err;
});
Still your example doesn't make much sense. You should never use done, when you return a promise. If you want to resolve initialized promise with internally created chain of promises, you should just resolve it as:
resolve(doSomething()
.then(function() {
console.log('1');
return doSomething1();
})
....
.then(function() {
console.log('N');
return doSomethingN();
}));
There's no need for internal error handling, leave that to consumer of promise which you return.
And other point. If when creating new promise you know it will be resolved with other one, then there's no logical reason to create such promise, just reuse one you planned to resolve with. Such error was also coined as deferred anti-pattern
You should consider doing this:
function foo() {
// Calling .then() on a promise still return a promise.
// You don't need Q.Promise here
return doSomething()
.then(function(doSomethingResult) {
console.log('1');
return doSomething1();
})
.then(function(doSomething1Result) {
console.log('2');
return doSomething2();
})
.then(function(doSomething2Result) {
console.log('3');
return doSomething3();
});
}
foo()
.then(function(fooResult) {
console.log(fooResult); // fooResult should be what is returned by doSomething3()
})
.catch(function(err) {
console.error(err); // Can be thrown by any
})
.done(function() {
console.log('I am always executed! error or success');
});
If you want to return a promise, in most cases it does not make much sense to use catch (unless you want to recover potential errors). It never make sense to use done in a method returning a promise. You would rather use these methods at the very end of the chain.
Notice that doSomethingX() can return either a value, or a promise, it will work the same.
You can make it work by resolving promise in your last then callback.
function foo(){
return doSomething()
.then(function() {
console.log('1');
return doSomething1();
})
.then(function() {
console.log('2');
return doSomething2();
})
.then(function() {
console.log('3');
return doSomething3();
})
}
Consider using bluebird for promises. It has many useful features as compared to any other promise library. You may find it difficult to begin it, but once you get hold of it you're going to love it.

Confusion on promise objects

I've read about promise objects and infact have worked on promise objects but still I must say I'm not clear on the basics.
$http.get('/someURL').then(function(response) {
// do something
}, function(error) {
});
People say that .then returns a promise object. In the above example $http.get() returns a promise object. So what does this line mean? Does it mean promise.promise?? (promise object returned by $http.get() dot promise returned by .then)?
Can anyone please clarify?
$http.get() returns a promise. You can then call .then() on that promise. .then() returns yet another promise that you aren't actually using in your code.
For example, you could do this:
var p = $http.get('/someURL').then(function(response) {
// do something
}, function(error) {
// do something on error
});
// p is a promise
p.then(function() {
// more code here that runs after the previous code
});
Or, you could do:
$http.get('/someURL').then(function(response) {
// do something
}, function(error) {
// do something on error
}).then(function() {
// more code here
});
So, each .then() handler returns yet another promise so you can chain as many times as you want. One particularly useful feature is that if you return a promise from a .then() handler callback, then the promise that the .then() handler already returned will inherit that promise you return from the callback like this:
$http.get('/someURL').then(function(response) {
return $http.get('/someOtherURL');
}, function(error) {
// do something on error
}).then(function(secondResponse) {
// will get here when both http.get operations are done
});
One of the cool features of promises is that they can be linked together. The $http.get() returns a promise which you have called the then on. That then also returns a promise and allows you to do additional things in another then statement. For instance:
function myGet() {
return $http.get('myRoute').then(
function(res) {
//do something
return res;
}, function(err) {
return $q.reject(err);
});
}
myGet().then(
function(res) {
//Do something else
}, function(err) {
//Handle Error
});
This can be very handy if you want to have a procedure happen after the myGet function either is succesful or has an error.

Is there a good way of short circuiting Javascript promises?

I'm a bit of a novice with promises/Deferreds. Is there a good pattern to handle the case where one might want to short circuit a chain of promises, for both success and error cases? In the error situation, I know you can chain a .then(null, function(error) {}) to the end and catch an error from any of the previous thens, but what if you want to handle an error in a more custom way and terminate? Would you specify a 'type' of error in an earlier error handler and return it via a new promise, to be handled or skipped in the final error handler? And what about a success case, where you want to terminate earlier in the chain (only conditionally firing off any later then's)?
Typically, the promise chain starts with a call to some asynchronous function as such:
var promise = callAsync();
If you are chaining a second async call, you probably do something like this:
var promise = callAsync()
.then(function(){
return callOtherAsync();
})
.then(function(){
return callSuccessAsync();
}, function(){
return callFailAsync();
});
As a result of chaining, promise now contains the final promise which completes when callFinalAsync()'s promise completes. There is no way to short circuit the final promise when using this pattern - you can return a failed promise along the way (for instance, rather than returning the result of callOtherAsync) but that requires the failed promise to progress through the chain (thus causing callFailAsync to be called).
You can always fulfill or reject the promise from within the callbacks as such
var promise = callAsync()
.then(function(){
if(fail){
promise.reject();
//no way to halt progression
}else{
return callOtherAsync();
}
})
.then(function(){
return callSuccessAsync();
}, function(){
return callFailAsync();
});
however, this will not prevent calls to callFailAsync(). Some Promise/A implementations expose a stop method for just this purpose. With stop, you could do this:
var promise = callAsync();
.then(function(){
if(fail){
this.stop();
promise.reject();
}else{
return callOtherAsync();
}
})
.then(function(){
return callSuccessAsync();
}, function(){
return callFailAsync();
});
Which depends on having access to the intermediate promise with this. Some Promise implementations forbid that (forcing this to be window/null/etc), but you can deal with that with a closure.
TL;DR: Promise/A spec doesn't provide a chain short circuit function, but it's not hard to add one.
not sure about jQuery but at least in any Promises/A+ you can just throw:
.then(function() {
if (skip) {
throw new Error("skipping");
}
})
//Chain of thens
.then(...)
.then(...)
.then(...)
.then(...)
.catch(function(){
//skipped here
});
I assume your use case looks like:
promise
.then(a)
.then(b); // We want to have an option to break here
.then(c)
.done(d)
Logical way to handle this is:
promise
.then(a)
.then(function (result) {
if (something) throw new Error("Do not proceed!");
return b(result).then(c).then(d);
}).done();
If you don't like nesting, you may compose b(result).then(c).then(d) as outer function.
I had this exact problem in my application, and achieved short-circuit/cancellation through use of a simple cancellation token object that can be checked for in a Promise's exception/rejection handler callback. Maybe not the most elegant solution, but seems to work well enough without the need for additional libraries or alternate/non-standard Promise implementations
const cancellationToken = {};
somePromiseReturningMethod(...)
.then(doSomething)
.then(doSomethingElse)
.catch(err => {
if (err === cancellationToken)
{
// handle cancellation here and return
}
// handle "regular" errors here (show/log a message, etc)
});
function doSomething(dataFromPromise)
{
// check for whatever condition should result in cancellation/short-circuit
if (...)
{
return Promise.reject(cancellationToken);
}
// carry on as normal...
}

Categories

Resources