jQuery deferred chaining problems

2020-08-02 01:47发布

Ok, I am probably missing something obvious, and although I've tried to find a similar example, I can't find one quite like what i'm wanting to do. I'm needing a series of ajax calls to run in a particular order. I'm using the following code to finalize a transaction:

showStandbyDialog();
    $.when(function(){console.log('Paying Charges due before transaction');})
        .always( this.applyCredit(parseFloat($(this.currentChargesTarget).html())) ) // Pay charges due before transaction
        .always(function(){console.log('Applying renewals');})
        .always( this.applyRenewals() ) // Apply Renewals
        .always(function(){console.log('Paying renewal charges');})
        .always( this.applyCredit(this.renewCart.length * this.renewCost) ) // Pay renewal charges
        .always(function(){console.log('Applying checkouts');})
        .always( this.applyCheckOut() ) // Apply checkouts
        .always(function(){console.log('Paying checkout charges');})
        .always( this.applyCredit(this.cart.length * this.checkOutCost) ) // Pay checkout charges
        .always(function(){console.log('Applying card replacement');})
        .always( this.applyCardReplacement() ) // Apply card replacement
        .always(function(){console.log('Paying leftover charges');})
        .always( this.applyCredit(this.cardCost) ) // Pay leftover charges
        .always(function(){console.log('Finalizing Transaction');})
        .always( function(){ updateCharges(); bfwd.Patron.Transaction.reset(); hideStandbyDialog(); } ); // Reset Transaction and clear standby dialog

Now I have tried, .done, .then, and just about .anything() but the console.log() code in the handle function of this.applyCredit() ALWAYS logs after the console.log('Finalizing Transaction'). Every this.function() call returns a jquery deferred method in case you were wondering.

2条回答
你好瞎i
2楼-- · 2020-08-02 02:20

OK, having figured out my first answer to how to do what you originally asked, here's my second answer. Don't do it.

Don't use the client to sequence an eight step financial process on the server. Heck, don't use it to sequence a two step financial process on the server. What happens when the server goes down, the user closes his/her laptop, or loses Internet connectivity? You end up with maybe steps 1-3 done or 1-5 done out of a sequence that was supposed to execute as a unit.

Make ONE AJAX call to the server and provide the needed information for it to do all eight steps. On the server use a transaction supported by your server if possible or use a database transaction to make sure that all of the operations succeed together or they all fail together and everything gets rolled back to its original state if there is any failure. Then you can report on the results of what happened, including details on each step if you need or want them, back to the client.

查看更多
做自己的国王
3楼-- · 2020-08-02 02:32

I completely remade my jsFiddle here: http://jsfiddle.net/JohnMunsch/nxPn3/

About 10 times :)

.always() was returning the same Deferred object (or Promise) that had completed in the first .when() to all of the other .always() functions. So they went off in rapid fire succession. It looked roughly like this.

Step 1: log.
Step 2: kick off ajax call, log, kick off ajax call, log, kick off ajax call, log, etc. etc.

I didn't really see it until I put timers in each of my "ajax call" functions to make them delay a while. Then I kept having the problem of, how do I do this without nesting each next step in the previous step's .done() function?

My solution to that one may not be the most graceful, but it worked. I created a complete set of Deferred objects up front and I use them to prevent each successive step from starting until the previous step is either marked resolved or rejected.

Here's a excerpt from that:

console.log('Paying Charges due before transaction');
var step1 = applyCredit("parseFloat($(this.currentChargesTarget).html())");
var step2 = new $.Deferred();
var step3 = new $.Deferred();

$.when(step1).done(function(){
    console.log('Applying renewals');
    $.when(applyRenewals()).done(function () {
        step2.resolve();
    });
}).fail(function () { step2.reject() });

$.when(step2).done(function(){
    console.log('Paying renewal charges');
    $.when(applyCredit("some subcalc")).done(function () {
        step3.resolve();
    });
}).fail(function () { step3.reject() });

Now when you run the jsFiddle, each function call marches along in a perfect row. The second doesn't start until the first has finished and the third can't begin until the second is done...

查看更多
登录 后发表回答