New $ .Deferred object with old callbacks

Please forgive me if this is a stupid question. I tried for hours and my brain just stopped working.

I have a system like this which consists of three AJAX calls. The server's response to the first call is usually 200 Success; but the second and third requests are fragile because they are image uploads and on the server side I have so many validation rules that client images fail most of the time.

window.AjaxCall = function () {
    // to pass to $.ajax call later
    this.args = arguments;

    // xhr status
    this.status = null;

    // xhr results (jqXHR object and response)
    this.xhrResponse = {};

    this.dfr = new $.Deferred();

    // to provide an easier interface
    this.done = this.dfr.done;
    this.fail = this.dfr.fail;
    this.then = this.dfr.then;
};

AjaxCall.prototype.resetDfr = function () {
    this.dfr = new $.Deferred();
};

AjaxCall.prototype.resolve = function () {
    this.dfr.resolve(
            this.xhrResponse.result,
            this.xhrResponse.jqXHR
    );

    this.resetDfr();
};

AjaxCall.prototype.reject = function () {
    this.dfr.reject(
            this.xhrResponse.jqXHR
    );

    this.resetDfr();
};

AjaxCall.prototype.query = function () {
    var _this = this;

    // if query hasn't run yet, or didn't return success, run it again
    if (_this.status != 'OK') {
        $.ajax.apply(_this, _this.args)
                .done(function (result, textStatus, jqXHR) {
                    _this.xhrResponse.result = result;
                    _this.xhrResponse.jqXHR = jqXHR;
                    _this.resolve();
                })
                .fail(function (jqXHR) {
                    _this.xhrResponse.jqXHR = jqXHR;
                    _this.reject();
                })
                .always(function (a, b, c) {
                    var statusCode = (typeof c !== 'string'
                            ? c
                            : a).status;

                    if (statusCode == 200) {
                        _this.status = 'OK';
                    }
                });
    }

    // if query has been run successfully before, just skip to next
    else {
        _this.resolve();
    }

    return _this.dfr.promise();
};

      

AjaxCall

the class is listed above and I am making three consecutive calls like this:

var First = new AjaxCall('/'),
        Second = new AjaxCall('/asd'),
        Third = new AjaxCall('/qqq');

First.then(function () {
    console.log('#1 done');
}, function() {
    console.error('#1 fail');
});

Second.then(function () {
    console.log('#2 done');
}, function() {
    console.error('#2 fail');
});

Third.then(function () {
    console.log('#3 done');
}, function() {
    console.error('#3 fail');
});

var toRun = function () {
    First.query()
            .then(function () {
                return Second.query();
            })
            .then(function () {
                return Third.query()
            });
};

$('button').click(function () {
    toRun();
});

      

These codes are in a test environment. And by testing the environment I mean a simple HTML page and basic server support for debugging.

  • Homepage (/) always returns 200 Success.
  • / asd returns 404 Not found for first 3 times and 200 successes once as a pattern (i.e. three 404s -> one 200 -> three 404s -> one 200 -> three 404s -> ...).
  • / qqq returns 404 Not all the time.

When I only click one button on the page, the first request returns success and the second returns as expected. When I click the button a second time, the first request is skipped because it was successful the last time, and the second one again, as expected.

The problem is here:

  • before using a methodresetDfr

    because it is dfr

    allowed or denied, it no longer responds to methods resolve

    and reject

    .
  • When I call the method resetDfr

    in the way I show in the example
    , it dfr

    may be allowed or denied again, but the callbacks of the old dfr

    one are not associated with the new object dfr

    , and I couldn't find a way to clone the old callbacks into the new one dfr

    .

What would your suggestion be to accomplish what I am trying to do here?

+3


source to share


1 answer


Promises represent a single time-related value. You cannot conceptually "reuse" a deferred or reset it - as soon as it goes into it. There are constructs that generalize promises to multiple values ​​(for example, observables), but in this case they are more complex - it's probably better to just use one deferred request.

jQuery AJAX already provides a promise interface. Your code is mostly redundant - you can and should consider using an existing tool.

Let's look at $.get

:

  • It already returns a promise, so you don't need to create your own deferred ones.
  • It already uses the browser cache, unless your server disables HTTP caching, or the browser refuses it, only one request will be sent to the server after receiving the correct response (assuming you haven't explicitly passed {cache: false}

    it to parameters.

If you are sending post requests, you can use $.post

or more generally $.ajax

for arbitrary parameters.



This is how your code looks like this:

$("button").click(function(){
    var first = $.get("/");
    var second = first.then(function(){
       return $.get("/asd");
    });
    var third = second.then(function(){
       return $.get("/qqq");
    });
});

      

The reason I put them in variables is so that you can later expand the result by doing first.then

etc. It is quite possible to do this in the same chain as well (but you lose access to the previous values ​​if you do not explicitly store them.

For the record - this was not a stupid question :)

+1


source







All Articles