0

I have a function which returns a promise. I create a jQuery deferred for this purpose, which might be resolved/rejected in custom ways, depending on implementation.

One implementation uses an AJAX call, and there I'd like to redirect or queue the failure/resolution of the AJAX promise to the one which was created earlier. This means that whenever the AJAX call has a resolve/reject/progress, the deferred should trigger its own resolve/reject/progress too with the same arguments.

Here is some dummy sample code.

function Test() {
}

Test.prototype.doSomething() {
  this._deferred = $.Deferred();
  this.doSomethingImpl();
  return this._deferred;
}

var test = new Test();
test.doSomethingImpl = function() {
  var ajax = $.post(...);
  // resolve/reject/progress here the this._deferred based on the ajax promise
}

I know I can do it in a verbose way using the AJAX done, fail and progress callbacks, and manually call my deferred's corresponding method (resolve, reject or progress), but I'm seeking for kind of a one-liner, if there is any.

EDIT

Here is a code which is similar to the real one, using knockoutjs.

function GridViewModel() {
  var self = this;

  self.pageIndex = ko.observable(0);
  ...
  self._refreshRequest = ko.observable(null).extend({ rateLimit: { timeout: 200, method: "notifyWhenChangesStop" } });

  self._doRefresh = function() {
    $.ajax(...)
     .done(result) { // update rows, etc. }
     .then(
        function(r) { self._refreshPromise.resolve(r); },
        function(r) { self._refreshPromise.reject(r); },
        function(r) { self._refreshPromise.progress(r); }
      )
     .always(function() { self._refreshPromise = null; }
    // here I used the obvious verbose redirecting    
  }

  ...

  ko.computed(function() {
    var pageIndex = self.pageIndex();
    if (ko.computedContext.isInitial()) return;
    this.refreshRequest("Paging");    
  });

  ko.computed(function() {
    var refreshRequest = self.refreshRequest();
    if (ko.computedContext.isInitial() || !refreshRequest) return;
    self._doRefresh(refreshRequest);
  }
}

GridViewModel.prototype.Refresh = function(type) {
  this._refreshPromise = this._refreshPromise || $.Deferred();
  this._refreshRequest(type);
  return this._refreshPromise;
}

This code is a snippet of a complex data grid viewmodel class, and the fancy refresh solution is there to ensure that refreshing is throttled.

Zoltán Tamási
  • 12,249
  • 8
  • 65
  • 93

1 Answers1

1

Yes, it would be possible to redirect the resolution (in a perfect world1, just deferred.resolve(promise)), but it's completely unnecessary. Don't create deferreds when you're already calling something that produces a promise for you - avoid the deferred antipattern! You can simply return that very promise:

Test.prototype.doSomething = function() {
    return this.doSomethingImpl();
};

var test = new Test();
test.doSomethingImpl = function() {
    var ajax = $.post(...);
    return ajax; // the promise
};

1) where jQuery follows the Promises/A+ specification and deferred.resolve accepts thenables

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thank you for the answer, I know about the anti-pattern. My real scenario is much more complex, and I cannot directly return the implementation. I'll accept your answer though, because I can go for the latest jQuery which is AFAIK Promise/A+ compliant. – Zoltán Tamási Nov 11 '16 at 12:47
  • @ZoltánTamási You pretty much always can get a promise for it. Please show us your real scenario if that is what you need help with. – Bergi Nov 11 '16 at 12:48
  • @ZoltánTamási to be exact, Promises/A+ only specifies behavior of `then`, not `resolve`, so I don't know whether they fixed that one as well in 3.0. But use any ES6-compatible promise library and `resolve(promise)` will do. – Bergi Nov 11 '16 at 12:49
  • Actually you're right, this particular `resolve` overload is still not documented yet. – Zoltán Tamási Nov 11 '16 at 12:51
  • My real code uses `knockoutjs` and its deferred updates feature, hence the execution is quite complex. And in fact the method where the AJAX is is not an external *implementation*, just another private method, so in real it's not breaking the responsibility of resolution. Maybe my sample code was too dummy, sorry, I didn't have a better idea to demonstrate the problem. – Zoltán Tamási Nov 11 '16 at 12:53
  • Regardless, the point is that your private method (or whereever the async stuff happens) should always *return a promise* instead of resolving some other people's deferreds, and whoever calls the method can then compose the promise (in `then` chains, `Promise.all`, as return values etc). – Bergi Nov 11 '16 at 12:58
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/127907/discussion-between-zoltan-tamasi-and-bergi). – Zoltán Tamási Nov 11 '16 at 15:00