zerkms
zerkms

Reputation: 254886

Nested promise handlers

Keeping in mind this statement:

Once the object has entered the resolved or rejected state, it stays in that state. Callbacks can still be added to the resolved or rejected Deferred — they will execute immediately.

from http://api.jquery.com/jQuery.Deferred/

What would you expect this code to produce:

var d = $.Deferred();

var a = function() {
    d.done(function() {
        console.log('inner');
    });
    
    console.log('outer');
};

d.done(a);

d.resolve();

?

I'm expecting it to be inner, then outer. Whereas it's not the case for any jquery version I checked.

Would you consider it as a bug or am I missing the point from the description?

Corresponding JSFiddle: http://jsfiddle.net/U8AGc/

UPD: some background for the question: I expect the a method to behave similarly regardless of how exactly it was invoked: just as an a() or d.done(a)

Upvotes: 2

Views: 146

Answers (2)

Bergi
Bergi

Reputation: 664297

I expect the a method to behave similarly regardless of how exactly it was invoked: just as an a() or d.done(a)

No. d.done(a) does not always call a() immediately - most prominently when d is not yet resolved.

Promises/A+ solves this ambiguity by requiring the handlers to be always fired asynchronously, i.e. here you could expect outer to precede inner in every case.

I'm expecting it to be inner, then outer

That is prevented by jQuery.Callbacks, which has an explicit firing flag for not immediately-executing handlers inside handlers; instead the added handlers are appended to the queue. It's a feature to prevent stack overflows as well, and it simplifies the fire function by "locking" it.

Would you consider it as a bug or am I missing the point from the description?

I'd consider not following Promises/A+ the bug :-) The description just didn't handle this - quite uncommon - special case.

I'd say its point was that handlers on settled deferreds are still automatically executed even after the resolution, by "immediately" they didn't necessarily mean "synchronously from within .done" but rather "right away, as soon as possible".

Upvotes: 3

SomeKittens
SomeKittens

Reputation: 39522

Here's what's happening:

var a = function() {
    // the function logging 'inner' is *added* to the call stack
    d.done(function() {
        console.log('inner');
    });
    // 'outer' is logged
    console.log('outer');
};
// `a` has finished executing, then the anonymous function logs 'inner'

I've added a log with the stack trace* in your JSFiddle - you can see that the function a is nowhere to be found in the anon function that's logging inner - jQuery is calling it once a has finished excecuting.

The relevant line in the jQuery source is found here (notice that it's adding to a queue).

*Trace code from here

Upvotes: 1

Related Questions