Return already resolved promise
Solution 1:
@Eselk,
In my experience, the $.Deferred(function(def) {...});
construction is rarely needed, though I guess it can be quite useful in some circumstances.
Firstly, :
return $.Deferred(function(def) { def.resolve(); }).promise();
will simplify to :
return $.Deferred().resolve().promise();
Secondly, in DataService.makeRequest()
you can avoid the need for an explicit $.Deferred
by exploiting .then()
, as follows :
function DataService() {
var self = this;
self.makeRequest = function (functionName, data) {
return $.ajax({
type: "POST",
url: "WebService.asmx/" + functionName,
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
dataType: "json"
}).then(null, function (xhr, status, error) {
var ex;
try {
ex = eval("(" + xhr.responseText + ")");
ex.message = ex.Message;
ex.Message = undefined;
} catch (ex2) {
ex = {
message: "Invalid Response From Server:\r\n" + xhr.responseText
};
}
if (ex.message == "LoginRequired") {
app.viewModels.main.loginRequired(true);
} else {
app.showError(ex.message);
}
return ex.message;
});
};
}
The key aspects here are :
-
$.ajax()
returns a promise-compatible jqXHR object, which (on success/error) is immediately acted upon and modified by.then()
. -
.then(null, ...)
- causes a new promise to be passed on, resolved with the same values as the original promise returned by$.ajax()
. Thus, under the 'done' (ie ajax success) condition,.then()
is completely transparent. -
return ex.message;
- causes a new promise to be passed on, rejected withex.message
.
The nett effect should be identical to your original code though, IMHO, chaining .then()
is significantly cleaner than setting everything up inside a $.Deferred()
callback.
By the way, you may be able to avoid the need for eval("(" + xhr.responseText + ")")
etc by setting an appropriate HTTP header server-side such that your '.Message' appears directly as the status
argument (or xhr.status
?) of the fail callback. For example, in PHP you would do something like :
$message = "my custom message";
header("HTTP/1.1 421 $message");
exit(0);
I'm sure ASP offers the same capability.
IIRC, any 4xx series status code will do the job. 421 just happens to be one without a specific meaning.
Solution 2:
Simply use return $.when();
to return an already resolved promise.
If you don't pass it any arguments at all, jQuery.when() will return a resolved promise.
Reference: https://api.jquery.com/jquery.when/
Notes:
- This is the same as
return $.when(undefined);
which leads to the following rather cool trick which avoids any use of arrays andapply
.
If you want to wait for a variable number of promises to complete in parallel then you can use this pattern in a loop:
var promise; // Undefined is treated as an initial resolved promise by $.when
while (insomeloop){
promise = $.when(promise, newpromise);
}
then make a final call on completion with:
promise.then(function(){
// All done!
});
e.g.
var promise;$.when
var $elements = $('.some-elements');
$elements.each(function(){
var $element = $(this).fadeout();
promise = $.when(promise, $element.promise());
});
promise.then(function(){
// All promises completed!
});
The downsides are minor:
- each call to
when
wraps the previous promise in a new promise. A minor overhead and you no longer need to maintain and evaluate an array of promises. - no values can be directly passed through to the final function. As you typically do not want the return values from parallel promises, this is minor.
- failure of a single promise will abort the final wait early, so you need to ensure all promises will be fulfilled.