I can't understand why the rejection isn't passed through the promise chain, and I'm hoping someone can help me understand why. To me, attaching a function to a sequence of promises means I'm relying on the intent of the original promise to be fulfilled. This is hard to explain, so let me first show a code example of my problem. (Note: This example uses Node and the delayed node module. I tested this using Dojo 1.8.3 and got 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 result of running this operation is this output:
promise1 rejected promise2 resolved promise3 resolved
Well, to me, this result doesn't make sense. By appending to this Promise chain, each then implies the intention that it will depend on the successful resolution of d1 and the result being passed along the chain. If the promise in promise1 does not receive a wins value, but an err value in its error handler, how can the next promise in the chain call its success function? It cannot pass a meaningful value to the next Promise because it does not get the value itself.
I can describe my idea another way: There are three people: John, Ginger and Bob. John owns a widget store. Ginger walked into his store and asked for a bag of widgets in various colors. He didn't have them in stock, so he sent a request to his dealer to have them shipped to him. Meanwhile, he gives Ginger a rain check, saying he owes her the bag of widgets. Bob spots Ginger getting the widgets and asks him to get the blue widget when she's done with them. She agreed and gave him a note saying she would agree. Now, John's dealer cannot find any widgets in their supply and the manufacturer no longer makes the widgets, so they notify John, who in turn notifies Ginger that she cannot obtain the widgets. How can Bob get the blue widget from Ginger when he doesn't get anything himself?
My third, more realistic view on this issue is this. Let's say I have two values that I want to update to the database. One depends on the id of the other, but I can't get the id until I insert it into the database and get the result. On top of that, the first insert depends on the database query. The promise returned by the database call is what 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 case, if db.query fails, it will call the first then's err function. But then it calls the success function of the next promise. Although this Promise expects the result of the first value, it gets an error message from its error handling function.
So, my question is, if I have to test for errors in the success function, why do I have an error handling function?
Sorry, this article is too long. I just don't know how to explain it another way.
Updates and Corrections
(Note: I deleted a reply I had made to some comments. So if someone commented on my reply, their comment might appear out of context now that I've deleted it. Sorry about that , I tried to keep it as short as possible.)
thanks for your replies. I first want to apologize to everyone for how poorly my question was written, especially my pseudocode. I was a little too aggressive in trying to keep it short.
Thanks for the reply Bergi, I think I found the error in my logic. I think I may be overlooking another issue that is causing the problems I'm having. This may cause the promise chain to work differently than I thought. I'm still testing different elements of the code, so I can't even form a proper question to see what I'm doing wrong. However, I did want to update everyone on the situation and thank you for your help.
@Jordan First, as commenters pointed out, your first example will definitely produce the results you expect when using a lazy library:
Secondly, even if it produces the output you suggest, it does not affect the execution flow of the second code snippet, which is a bit different and more like:
And, if the first promise is rejected, only output:
However (getting to the most interesting part) even though the deferred library definitely returns
3 x returned
, most other promise libraries will return1 x returned, 2 x resolved
(This leads to the assumption that you obtain these results by using some other Promise library).Also confusing is that other libraries behave more correctly. Let me explain.
In the synchronous world, the counterpart to "promise rejection" is
throws
. So semantically speaking, asyncdeferred.reject(new Error())
in sync is equal tothrow new Error()
. In your example you don't throw errors in the synchronous callback, you just return them, so you switch to the success flow where errors are the success values. To ensure rejection of further passes, you need to re-throw the error:Now the question is, why does the delay library treat the returned error as a rejection?
The reason is that rejection in delayed work is a little different. In the deferred lib, the rules are:When an error instance occurs, the promise will be rejected, so even if you do
deferred.resolve(new Error())
it will work like deferred.reject(new Error()), if you try to execute deferred.reject(notAnError), it will throw an exception indicating that the Promise can only be rejected with errors. This clearly shows why the error returned from thethen
callback rejects the promise.There is some valid reasoning behind the defer logic, but it still doesn't fit with how
throw
works in JavaScript, so this behavior is scheduled to change in the v0.7 release of defer.Short summary:
To avoid confusion and unexpected results, just follow the rules of good practice:
Adhere to the above and you will get consistent and expected results in Deferred and other popular Promise libraries.
No. What you are describing is not a chain but simply attaching all callbacks to
d1
. However, if you want to link something usingthen
, the result ofpromise2
depends on the resolution ofpromise1
and the resolution ofpromise1
and then the callback handles it .The documentation states:
.then
Methods typically look atPromises/A specification(or more strictlyPromises/A). This means that the Promise returned by the callback shell will be assimilated to the resolution of Promise2, and if there is no success/error handler, the corresponding result will be passed directly to Promise2 code> - so you can simplyomit the handlerto propagate errors.However, if the error ishandled, the resulting
He shouldn't be able to. Without an error handler, he would only perceive the message that there are no widgets left ((from John) from Ginger). However, if Ginger sets an error handler for this situation, if John or his dealer With no blue widgets left, she can still fulfill her promise to give Bob a widget from her own cabin and give him a green widget. To convert the error callback into a meta, return the error from the handler like saying "If no widgets are left, just give him a comment that no widgets are left - this is the same as the required widget The parts are just as good”. ...This means the error has been handled there. If you don't, just omit the error callback. BTW, your success callbacks don'tpromise2
is considered fixed and will be fulfilled with that value. If you don't want this to happen, you mustre-throw theerror
just like in a try-catch clause. Alternatively, you can return a (pending) rejected promise from the handler. Not sure what Dojo's rejection is, but:return
Or, since you don't need a closure to access the result value of the previous callback, even:the promise they are creating, so they appear to be useless. the correct one is: