Why is the Promise constructor so verbose? - javascript

This is not a "how does it work" but "why is it this way - what am I missing" question:
I think I've gotten the hang of the javascript Promise construct - very clever, simple and nice. I love it. But I do wonder - and I'm sure someone can give me a good answer - why is the Promise constructor different from the .then() method?
some_promise.then(
function(){
if (some_condition) {
return 'It works!';
} else {
throw Error('Noooooo!');
}
).then(
function(message){
console.log('I was told: '+message);
}
).catch(
function(err) {
console.log('Dang, it broke!');
}
)
works like a charm, but in order to get the original promise to work, you HAVE to do:
var some_promise = new Promise(function(resolve, reject){
if (some_condition) {
resolve('It works!');
} else {
reject(Error('Noooooo!'));
}
});
I had a hard time wrapping my head around promises at the beginning. The concept was simple enough, but the examples (like above) confused me greatly, and I figured out it was this difference that got me - it seems inconsistent to me. Is there any reason why the standard isn't more along the lines of:
//Modification to the standard
Promise.to = function(action){
return new Promise(function(resolve, reject){
resolve(true);
}).then(action);
}
//Would result in a new Promise being constructed / used like this:
var some_promise = Promise.to(function(){
if (some_condition) {
return 'It works!';
} else {
throw Error('Noooooo!');
}
}).then(
function(message){
console.log('I was told: '+message);
}
).catch(
function(err) {
console.log('Dang, it broke!');
}
)
I don't do this, because I think it's a bad idea to modify the base "classes" in javascript, but the example above seems to be working.
What would be lost if the standard worked this way? I guess something, but what am I not getting? It seems to be simpler to grasp, when getting aquainted with this concept and it's less verbose.

The entire purpose of the resolve and reject callbacks is that they can be called at any time, even after the construction function has finished. This makes is useful for asynchronous operations which can be started at construction but not completed before the constructor completes. As your examples only deal with the synchronous case, this advantage is lost. (...actually, it's more than an advantage... it's the entire reason for the existence of Promises).
You can also return promises from your continuations, such that the subsequent continuation (or .then clause) will only run when the previously returned Promise has completed. All good, but you'd be left with the problem of how to make an async promise in the first place without the original interface.

The point of the promise constructor is indeed to allow asynchronous promise completion.
However, if you are not doing that, then what you describe is present in many promise libraries. For example, in bluebird it is Promise.try:
var some_promise = Promise.try(function(){
if (some_condition) {
return 'It works!';
} else {
throw Error('Noooooo!');
}
}).then(
function(message){
console.log('I was told: '+message);
}
).catch(
function(err) {
console.log('Dang, it broke!');
}
)
Also, if you want to use then, you can just start off with an already resolved promise by using Promise.resolve:
Promise.resolve(undefined).then(
function(){
if (some_condition) {
return 'It works!';
} else {
throw Error('Noooooo!');
}
).then(
function(message){
console.log('I was told: '+message);
}
).catch(
function(err) {
console.log('Dang, it broke!');
}
)
But, to reiterate, this is only relevant if you do not actually need the asynchronous nature of the promise constructor - like in the examples you gave above. It is not rationale for making the Promise constructor any different from what it is, because it has a well defined purpose and need.

Related

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.

Recovering from rejected promises in JS

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

Do I have to fulfil my JavaScript promises?

In a Node.js environment if I do this:
var doNoResolve = true;
function a() {
return new Promise(resolve => {
if (doNotResolve) {
return
}
resolve(10);
});
}
a().then(() => {
// I don't want this getting fired
});
On an incoming request, is this a memory leak? If I was using a plain old callback everything would turn out just fine if I didn't execute whatever callback was supplied, but this feels like it might not be... the very name promise implies this is somewhat wrong.
If I had to I could return a "fake promise" (return { then: () => {} }) inside function a() rather than a "real promise" if doNotResolve was true, but that feels a bit gross.
The particular use-case is that of an isomorphic React.js application where I don't want HTTP requests actually getting made (but I do want my stores to update to a state that causes, say, a loading icon to appear).
Why would you do that instead of rejecting?
The benefit of promises is that they allow both resolving and rejecting, which:
Doesn't fire the then handler (unless you provide two callbacks, which is considered bad practice)
Does fire the catch handler, which explicitly handles errors
Still fire the finally handler
You can simply do:
function a() {
return new Promise((resolve, reject) => {
if (doNotResolve) {
reject(new Error('Oh noes!'));
}
resolve(10);
});
}
Any good Promise implementation will give you a stacktrace from the location you called reject to help you debug async code, as well as calling any catch/finally handlers:
a().then(val => {
console.log('Got data:', val);
}).catch(err => {
console.error(err);
}).finally(() => {
console.log('Done!');
});
Never rejecting or resolving a promise will, depending on your implementation, leave it on the stack of pending promises and very likely throw or log an error when your page unloads or the node promise exits. I know Bluebird will complain if you've left any promises pending, since it typically indicates a bug within the async part of your code.

Chaining JavaScript Promises that include nested AJAX calls

I'm looking to replace some of my existing code with JavaScript Promises and I just want to confirm that I'm not doing it wrong (or maybe there are better ways). I'm using the es6-promise library.
What I have are three functions that I've updated to use JavaScript Promises (what was previously a nested, callback-ey mess). The functions are actually supposed to have dual modes i.e. I can use them like regular functions and have them return a result, or I could chain them together.
function func_1()
{
return new Promise(function(resolve, reject){
if(condition)
{
resolve('1');
}
else
{
console.log('start');
resolve(ajaxRequest(url));
}
});
}
function func_2(data)
{
return new Promise(function(resolve, reject){
if(condition)
{
resolve('2');
}
else
{
console.log(data.response);
resolve(ajaxRequest(url));
}
});
}
function func_3(data)
{
return new Promise(function(resolve, reject){
if(condition)
{
resolve('3');
}
else
{
console.log(data.response);
resolve(ajaxRequest(url));
}
});
}
func_1().then(func_2).then(func_3).then(function(data){});
The if is to check whether data is cached in localStorage/sessionStorage, and if so the function just returns the result. However, in circumstances where I am unsure if the values have been cached (e.g. first run of the script), then I plan to chain them and then have each subsequent function persist the result from its preceeding one to localStorage/sessionStorage (hence the promise chain on the last line). The final then gives me an opportunity to persist the data from func_3.
Based on the tests I have run, everything seems to be working ok, but I was just wondering if this was the best way of doing this? And how do I handle the AJAX errors that could happen on one or more of the 3 listed functions?
Note: My AjaxRequest function also uses the same Promise mechanism and 'resolves' a full XHR on success, and 'rejects' the same full XHR on failure/error.
EDIT
After a tip from #Bergi, I've updated the code to look like this (and it works just as well):
function func_1()
{
if(condition)
{
return Promise.resolve('1');
}
else
{
console.log('start');
return ajaxRequest(url);
}
}
function func_2(data)
{
if(condition)
{
return Promise.resolve('2');
}
else
{
console.log(data.response);
return ajaxRequest(url);
}
}
function func_3(data)
{
if(condition)
{
return Promise.resolve('3');
}
else
{
console.log(data.response);
return ajaxRequest(url);
}
}
func_1().then(func_2).then(func_3).then(function(data){})['catch'](function(err){console.log(err)});
everything seems to be working ok, but I was just wondering if this was the best way of doing this?
You should never really need to use the Promise constructor except on the lowest level (i.e. when promisifying that ajax request). Your functions should simply be written like this:
function func_1() {
if (condition) {
return Promise.resolve('1');
} else {
console.log('start');
retrun ajaxRequest(url);
}
}
Strictly speaking, the func_2 and func_3 might even do return '2', the then method for which they are used as callbacks can cope with that. Of course it is cleaner to always return a promise object.
And how do I handle the AJAX errors that could happen on one or more of the 3 listed functions?
Pass a second function to then, or use catch. This callback will get called when the promise is rejected, and should handle the exception.

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