Skip to main content

Bug Tracker

Side navigation

#10286 closed enhancement (invalid)

Opened September 15, 2011 07:17PM UTC

Closed September 19, 2011 07:57AM UTC

Last modified September 21, 2011 07:31AM UTC

AJAX prefilters and callbacks have no opportunity to influence Deferred object resolution

Reported by: dthm5kvf6e@snkmail.com Owned by:
Priority: undecided Milestone: None
Component: unfiled Version: 1.6.4rc1
Keywords: Cc: jaubourg
Blocked by: Blocking:
Description

While I can supply success, error, and completion callbacks to any $.ajax() call, by the time these callbacks are invoked, the default AJAX transport object has already resolved or rejected the embedded promise/Deferred object on my behalf. Any code that was waiting on said resolution will be invoked whether I want it to or not.

The only way that I can pre-empt this behavior is by defining my own AJAX transport whose "send" method will receive a pointer to the default "done" method which performs this task. Since there is no way for such a custom transport to inherit or invoke the remainder of the default transport's logic without duplicating core library code, this is not a practical solution, either.

Attachments (0)
Change History (5)

Changed September 19, 2011 07:57AM UTC by jaubourg comment:1

cc: → jaubourg
resolution: → invalid
status: newclosed

It's actually quite easy to override the promise behaviour of the jqXHR object in a prefilter. You just need to use the promise method of a new Deferred object with the jqXHR object itself as the target objet parameter:

$.ajaxPrefilter(function( options, originalOptions, jqXHR ) {
    var originalPromise = jqXHR.promise(),
        newDefer = $.Deferred(function( defer ) {
            defer.promise( jqXHR );
            jqXHR.success = defer.done;
            jqXHR.error = defer.fail;
            // Need a little bit more work to handle complete
        });
    originalPromise.done(function() {
        newDefer.resolve( /* whatever */ );
    });
});

Callbacks are not attached after prefilters are called so they will be attached to your new deferred this way. The jqXHR object hasn't been given to any external code yet (the beforeSend callback is called AFTER prefilters) so it's 100% safe.

Changed September 19, 2011 04:16PM UTC by anonymous comment:2

Thanks. That's pretty ugly, though.

Changed September 20, 2011 01:13PM UTC by jaubourg comment:3

Replying to [comment:2 anonymous]:

Thanks. That's pretty ugly, though.

Well, let's see:

  • You don't have to know about the internals of the transport layer
  • You don't have to worry about the dataType conversion layer
  • You don't need to worry about another prefilter doing something similar (everything will still work as intended)

I'd say it's pretty neat in its simplicity. You could even do it using pipe:

jqXHR.pipe( function() {
   // Handle success and eventually redirect
}, function() {
   // Handle error and eventually redirect
} ).promise( jqXHR );
// Aliases
jqXHR.success = jqXHR.done;
jqXHR.error = jqXHR.fail;

Of course, you still have to handle complete if you really need to but that's not that much of an hassle, is it?

Changed September 20, 2011 05:28PM UTC by anonymous comment:4

The API could make that process a whole lot less retarded by vending a reference to the internal "done" method, but that would involve actual thought in place of snark on your part, so I shant be holding my breath.

Changed September 21, 2011 07:31AM UTC by jaubourg comment:5

_comment0: Giving access to the internal "done" method (whatever you exactly mean by that) is insufficient and dangerous. A key in the design of Deferreds is to ensure no-one will bork your Deferreds to death. The fact that Deferreds methods are "detachable" (lexically bound) allows a lot of flexibility without the need to compromise the Deferred's internal state by having the Deferred's guts wide opened. Please, stop obsessing about your immediate problem and just ponder this for a mere second. \ \ No, really, stop being mad at me/the screen/the world right now, re-read the paragraph above and do think about it. \ \ I know it's always difficult to understand that cutting corners for every specific edge-case is NOT good design. Making so said edge-case can be solved using a reasonable ammount of code without compromising everyone else's code (at least not that easily, I know we're still in JavaScript) is the ultimate goal. \ \ Now bring on the insults!1316597811802338

Giving access to the internal "done" method (whatever you exactly mean by that) is insufficient and dangerous. A key in the design of Deferreds is to ensure no-one will bork your Deferreds to death. The fact that Deferreds methods are "detachable" (lexically bound) allows a lot of flexibility without the need to compromise the Deferred's internal state by having the Deferred's guts wide opened. Please, stop obsessing about your immediate problem and just ponder this for a mere second.

No, really, stop being mad at me/the screen/the world right now, re-read the paragraph above and do think about it.

I know it's always difficult to understand that cutting corners for every specific edge-case is NOT good design. Making so said edge-case can be solved using a reasonable amount of code without compromising everyone else's code (at least not that easily, I know we're still in JavaScript) is the ultimate goal.

Now bring on the insults!