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 method
resetDfr
because it isdfr
allowed or denied, it no longer responds to methodsresolve
andreject
. - When I call the method
resetDfr
in the way I show in the example , itdfr
may be allowed or denied again, but the callbacks of the olddfr
one are not associated with the new objectdfr
, and I couldn't find a way to clone the old callbacks into the new onedfr
.
What would your suggestion be to accomplish what I am trying to do here?
source to share
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 :)
source to share