Bug Tracker

Opened 12 years ago

Last modified 11 years ago

#10944 closed bug

$.ajax does not always return an object implementing the Promise interface — at Version 7

Reported by: fastfasterfastest Owned by:
Priority: low Milestone: 1.8
Component: ajax Version: 1.7.1
Keywords: 1.8-discuss Cc:
Blocked by: Blocking:

Description (last modified by jzaefferer)

The documentation of $.ajax states "The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the Promise interface...". This is not true in 1.7.1. As a consequence one cannot (safely) use the Promise programming model on the object returned by $.ajax.

If you use a beforeSend callback and the callback returns false, then $.ajax does not return an object that implements the Promise interface - $.ajax instead returns a boolean.

E.g., http://jsfiddle.net/67DJr/

$.ajax( '/echo/html' , { beforeSend: function(){ return confirm('Access resource?'); } })
    .done(function() { alert("success"); })
    .fail(function() { alert("error"); })
    .always(function() { alert("complete"); });

The above code causes a javascript error if user clicks Cancel.

Per documentation, $.ajax should always return an object that implements the Promise interface. In the case of beforeSend returning false, and thus cancelling or preventing the call from being made, it seems to make sense for $.ajax to return a Promise object that has been rejected.

Change History (7)

comment:1 Changed 12 years ago by sindresorhus

Component: unfiledajax
Priority: undecidedlow
Status: newopen

comment:2 Changed 12 years ago by fastfasterfastest

Rather than returning a Promise object that has been rejected, it may make more sense to return a Promise object that is in "pending" state and that will never be resolved nor rejected.

comment:3 Changed 11 years ago by jaubourg

Keywords: 1.8-discuss added

This is a backward compatibility issue. 1.4.x already returned false when beforeSend returned false.

I agree returning a rejected promise would be better, especially given $.when( false ) would give a resolved promise and cannot be used to "save the day" in this situation.

However, such a change would break existing code (though I dunno how many people rely on the existing behaviour -- our unit tests do at least).

I'll tag this as open for discussion for 1.8.

Last edited 11 years ago by jaubourg (previous) (diff)

comment:4 Changed 11 years ago by fastfasterfastest

One issue with returning a rejected promise is handling of errors.

Currently, the "fail functions" of an ajax promise will be called only if the ajax call errored. If $.ajax returns a rejected promise when beforeSend returns false, then those "fail functions" would be called as well - and they would (likely) need to determine when/why they got called (e.g. to present some information to the user), was it because beforeSend returned false or was it because the $.ajax call failed.

Also, if a rejected promise is returned when beforeSend returns false, then the "fail functions" of the ajax promise will be called, but the error callback in the ajax settings will not be called. I.e.:

$.ajax( '/echo/html' , {
    beforeSend: function(){ return confirm('Access resource?'); },
    error: function() { alert("will not be called if beforeSend returns false"); }
})
.fail(function() { alert("will be called if beforeSend returns false });

That would be a little "inconsistent" with the current behavior where the error callback in the ajax settings and the promise's fail functions will either all be called, or none will be called, depending on whether the ajax call succeeds.

Therefore, I think it may be better to return a promise that will never be resolved nor rejected if beforeSend returns false.

(Another possible alternative would be to no longer be able to cancel the call by beforeSend returning false, i.e. beforeSend's return value would be ignored.)

comment:5 in reply to:  4 Changed 11 years ago by jaubourg

Well, how useful would a pending Promise be? Do we really want to force users to check the Promise's state in order to determine what happened?

I'm not that shocked by the inconsistency of having fail callbacks added later on called whereas ones given in the error options were not. I'd rather see this as a feature.

Ignoring the return value of beforeSend is clearly not an option here.

And btw, same goes when aborting in a prefilter.

comment:6 Changed 11 years ago by dmethvin

Seems like a rejected promise would be the way to go, although I think we would want to document some way to determine it was rejected due to the beforeSend callback returning false. I agree a forever-pending state is not good.

comment:7 Changed 11 years ago by jzaefferer

Description: modified (diff)

+1, Could be considered a bug.

Note: See TracTickets for help on using tickets.