I have an existing project that has a lot of asynchronous functions that return promises. I'm adding some caching so that in some cases the asynchronous functions will complete synchronously and would like to make this code shorter/better if possible:
return $.Deferred(function(def) { def.resolve(); }).promise();
For example, I have a Data Service class that handles most AJAX requests that looks like this:
function DataService() {
var self = this;
self.makeRequest = function(functionName, data) {
return $.Deferred(function(def) {
var jsonData = JSON.stringify(data);
$.ajax({
type: "POST",
url: "WebService.asmx/" + functionName,
data: jsonData,
contentType: "application/json; charset=utf-8",
dataType: "json",
error: 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);
}
def.reject(ex.message);
}
});
}).promise();
}
}
Then I have a function in another class that currently always calls makeRequest:
self.deleteMe = function()
{
return app.dataservice.makeRequest('deleteItem');
}
I want to update the deleteMe function so that it doesn't always call makeRequest, and instead just does some synchronous work and then returns. It needs to return a promise though, because whatever called it will be expecting that, but it needs to be an "already completed/resolved promise". Currently I am using the first set of code above to do that. Seems like there must be a better way.
@Eselk,
In my experience, the
$.Deferred(function(def) {...});
construction is rarely needed, though I guess it can be quite useful in some circumstances.Firstly, :
will simplify to :
Secondly, in
DataService.makeRequest()
you can avoid the need for an explicit$.Deferred
by exploiting.then()
, as follows :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 thestatus
argument (orxhr.status
?) of the fail callback. For example, in PHP you would do something like :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.
Simply use
return $.when();
to return an already resolved promise.Reference: https://api.jquery.com/jquery.when/
Notes:
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:
then make a final call on completion with:
e.g.
The downsides are minor:
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.