Rejections are not propagated in chained promises
P粉193307465
P粉193307465 2023-10-23 17:50:47
0
2
2737

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.

P粉193307465
P粉193307465

reply all (2)
P粉155710425

@Jordan First, as commenters pointed out, your first example will definitely produce the results you expect when using a lazy library:

promise1 rejected promise2 rejected promise3 rejected

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:

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, if the first promise is rejected, only output:

promise1 rejected

However (getting to the most interesting part) even though the deferred library definitely returns3 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" isthrows. 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:

function (err) { console.log('promise1 rejected'); throw err; });

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 dodeferred.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 thethencallback rejects the promise.

There is some valid reasoning behind the defer logic, but it still doesn't fit with howthrowworks 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:

  1. Always reject promises with error instances (following the rules of the synchronous world, where throwing non-error values is considered bad practice).
  2. Reject the synchronization callback bythrowing anerror (returning an error does not guarantee rejection).

Adhere to the above and you will get consistent and expected results in Deferred and other popular Promise libraries.

    P粉376738875

    No. What you are describing is not a chain but simply attaching all callbacks tod1. However, if you want to link something usingthen, the result ofpromise2depends on the resolution ofpromise1and the resolution ofpromise1and then the callback handles it .

    The documentation states:

    .thenMethods 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 resultingpromise2is considered fixed and will be fulfilled with that value. If you don't want this to happen, you mustre-throw theerrorjust 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:

    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());
    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't

    returnthe promise they are creating, so they appear to be useless. the correct one is:

    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 a closure to access the result value of the previous callback, 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);
      Latest Downloads
      More>
      Web Effects
      Website Source Code
      Website Materials
      Front End Template
      About us Disclaimer Sitemap
      php.cn:Public welfare online PHP training,Help PHP learners grow quickly!