Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return already resolved promise

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.

like image 353
eselk Avatar asked May 20 '13 22:05

eselk


People also ask

How do I return a resolved promise?

resolve() method in JS returns a Promise object that is resolved with a given value. Any of the three things can happened: If the value is a promise then promise is returned. If the value has a “then” attached to the promise, then the returned promise will follow that “then” to till the final state.

What does returning a promise mean?

Wait until all promises have settled (each may fulfill or reject). Returns a Promise that fulfills after all of the given promises is either fulfilled or rejected, with an array of objects that each describe the outcome of each promise.

Can a promise be resolved more than once?

JavaScript Promises are powerful, and they become more powerful once we understand that a “resolved” Promise can be used more than once!

What does resolving a promise mean?

A resolved promise means, that the code handled by the promise is done, and the code in the callback passed to the then method is executed with the resolved value passed in.


2 Answers

@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 with ex.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.

like image 97
Beetroot-Beetroot Avatar answered Sep 29 '22 15:09

Beetroot-Beetroot


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 and apply.

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.
like image 25
Gone Coding Avatar answered Sep 29 '22 16:09

Gone Coding