0

I'm using jQuery for AJAX but Q elsewhere in our application so want to ensure that the Promise implementation is consistent.

I've wrapped the jQuery AJAX call with Q like so:

Q($.ajax(url, {
  type: 'get'
}));

And it works all fine for success responses. Now I want to add error handling, I want to have global error handling (since we have a consistent error response from our server), making an API like so:

var xhr = function (url) {
    return Q($.ajax(url, {
      type: 'get'
    }))
    .then(function (data) {
      return data;
    }, function (res) {
      //global error handling
    });
};

And I'd expect to use it like this:

xhr('...').then(function () { console.log('success'); });

The problem is when the global error handler runs the "success" method on the consumer of the xhr method is invoked.

How would you prevent that?

A runnable sample can be seen here - http://jsbin.com/gudifu/3/edit

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Aaron Powell
  • 24,927
  • 18
  • 98
  • 150
  • possible duplicate of [Promise's second .then() not failing](http://stackoverflow.com/q/23943322/1048572) – Bergi Mar 13 '15 at 04:21

2 Answers2

3

Due to jQuery's non-standard promises, you have to jump through hoops to pass on the error to a Q-coerced promise.

Firtly, jqXHR's error signature is (jqXHR, textStatus, errorThrown), and you typically want to pass on the second arg, textStatus.

Secondly, the two promise implementations are quite different with regard to error handling:

  • jQuery: propagates the error state unless a fresh, resolved promise is returned from a .then() error callback. .fail() is guaranteed to propagate the error state regardless of what is returned. An uncaught throw will terminate the event thread.
  • Q: marks an error as "handled" (ie send it down the success path) unless a .catch(), .fail() or .then() error callback rethows the error or throws a fresh one, in which case the promise continues down the error path.

Hence, you need to :

  • chain .then(null, errHandler) to your jQuery.ajax() call in order to report textStatus
  • throw an error from Q based on the jQuery error
var xhr = function (url) {
    return Q($.ajax(url, {
      type: 'get'
    }).then(null, function(jqXHR, textStatus, errorThrown) {
        return textStatus;
    })).then(function (data) {
        return data;
    }, function (textStatus) {
      //global error handling
      throw new Error(textStatus);
    });
};

You could alternatively throw a proper error from jQuery and simply rethrow it from Q.

var xhr = function (url) {
    return Q($.ajax(url, {
      type: 'get'
    }).then(null, function(jqXHR, textStatus, errorThrown) {
        return new Error(textStatus);
    })).then(function (data) {
        return data;
    }, function (err) {
      //global error handling
      throw err;
    });
};

The nett effect of the two solutions will be pretty close to identical. I think I'm right in saying that they will show a different error line number if the error was logged - the former stemming from Q and the latter stemming from jQuery.

Roamer-1888
  • 19,138
  • 5
  • 33
  • 44
1

Your problem is that the global error handler handles the error. As promises chain, your xhr(…) promise will just be fulfilled with whatever the error handler returned. It's like a catch, when an exception has been handled then normal execution resumes.

So you need to rethrow the exception from the handler so that the resulting promise gets rejected as well:

var xhr = function (url) {
    return Q($.ajax(url, {
        type: 'get'
    }))
    .then(null, function (res) {
        //global error handling
        throw res;
    });
};
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Appear to return same result at jsbin ? Is function being called twice ? – guest271314 Mar 13 '15 at 04:16
  • Yes. Appear to be called on `console` at "runner-*" piece ? `:1 fail runner-3.25.21.min.js:1 pass through fail ` ? Will try at stacksnippets, jsfiddle – guest271314 Mar 13 '15 at 04:20
  • @guest271314: I have no idea what you're talking about. OP's version logs `fail - pass through success` while mine logs `fail - pass through fail` as expected. – Bergi Mar 13 '15 at 04:23
  • Yes, appear did not interpret question correctly here . Read , viewed piece at OP and would have expected only single error response was, or should be desired returned result , reason . Re-read your answer and noticed "global error handler" – guest271314 Mar 13 '15 at 04:30