Kosmotaur
Kosmotaur

Reputation: 1786

Promises and recurrent AJAX calls

I am trying to come up with a function whose signature would be identical to jQuery.ajax. It is a separate function because depending on the HTTP status in the response it should either complete and resolve the promise returned, or issue a delayed subsequent AJAX request with identical parameters (hence the recurrence). While I have a solution that works, I feel like it's a promise anti-pattern, because I am explicitly calling $.Deferred() to represent the state of the process. Questions:

Please refer to the function I have:

callAPI = function(jqAjaxOptions, deferred) {
  if (deferred == null) {
    deferred = $.Deferred();
  }
  $.ajax(jqAjaxOptions).always(function(data, status, xhr) {
    var args;
    args = _.toArray(arguments);
    switch (xhr.status) {
      case 200:
        return deferred.resolve.apply(deferred, args);
      case 201:
      case 202:
        return setTimeout(function() {
          return callAPI(jqAjaxOptions, deferred);
        }, 2000);
      default:
        deferred.reject.apply(deferred, args);
        if (data.responseText) {
          return app.notifier.set(JSON.parse(data.responseText));
        } else {
          return app.notifier.set({
            title: "Couldn't contact data server.",
            content: "It seems the API server is down. Please contact the DAV team."
          });
        }
    }
  });
  return deferred.promise();
};

Upvotes: 2

Views: 890

Answers (1)

Bergi
Bergi

Reputation: 664297

Is it possible to reuse the „thenable” object returned by the $.ajax calls, as in chaining up the promises returned by subsequent calls?

Yes and No. You would loose the multiple arguments, and there's no way to emit progress events. It would look like this:

function timeout(ms) {
    var d = $.Deferred();
    setTimeout(d.resolve, ms);
    return d.promise();
}
function callApi(ajaxOptions) {
    function tryit () {
        return $.ajax(ajaxOptions).then(data) {
             if (this.status == 200)
                 return data;
             else if (this.status == 201 || this.status == 202)
                 return timeout(2000).then(tryit);
             else
                 return $.Deferred().reject(this.responseText
                   ? JSON.parse(this.responseText)
                   : {
                     title: "Couldn't contact data server.",
                     content: "It seems the API server is down. Please contact the DAV team."
                 });
        });
    }
    return tryit();
}

How would you go about incorporating a progress call on the promise chain each time another $.ajax is called?

Just call notify:

function callApi(ajaxOptions) {
    var deferred = $.Deferred();
    function tryit() {
        $.ajax(jqAjaxOptions).always(function() {
            switch (this.status) {
                case 200:
                    return deferred.resolveWith(this, arguments);
                case 201:
                case 202:
                    deferred.notify("Next try in 2s");
                    return setTimeout(tryit, 2000);
                default:
                    deferred.notify(this.responseText
                      ? JSON.parse(this.responseText);
                      : {
                            title: "Couldn't contact data server.",
                            content: "It seems the API server is down. Please contact the DAV team."
                    });
                    deferred.rejectWith(this, arguments);
            }
        });
    }
    tryit();
    return deferred.promise();
}

Upvotes: 1

Related Questions