1

I posted a similar question related to Ember.RSVP.all, but I've found the behavior identical to how Promise.all behaves. Please let me know if I'm breaking any rules by submitting this separately.

I'm trying to use Promise.all in the middle of a chain of promises. The example I have is much simpler than my use, but it demonstrates the issue. In the middle of a chain of promises, I have a set of promises that all need to resolve before the chain can continue - exactly what I understand Promise.all to be for.

Unfortunately, when I return the Promise.all object, the next promise in the chain runs immediately, without waiting for the promises passed to all().

I've set up a js fiddle to demonstrate in the best way that I can think of:

Notice that First and Second both resolve at almost exactly the same time, when Second should be after the 1s promise comes back. Third and fourth follow as expected.

http://jsfiddle.net/vqut9zy2/

Fiddle code looks like this:

function delayAjax(delay) {
    return $.ajax({
        url: '/echo/json/',
        data: {
            json: '',
            delay: delay,
        }
    });
}

delayAjax(1).then(function() {
    $('#first').addClass('red');
    var proms = [delayAjax(1), delayAjax(1)];
    return Promise.all(proms).then(function() {
        $('#onepointfive').addClass('red');
    });
}).then(function() {
    $('#second').addClass('red');
    return delayAjax(1);
}).then(function() {
    $('#third').addClass('red');
    return delayAjax(1);
}).then(function() {
    $('#fourth').addClass('red');
});

HTML

<div id="first">First</div>
<div id="onepointfive">One point five</div>
<div id="second">Second</div>
<div id="third">Third</div>
<div id="fourth">Fourth</div>
Community
  • 1
  • 1
Carl
  • 905
  • 5
  • 9
  • Might have something to do with jQuery not honoring the return value of `Promise.all` as promise. Removing the first Ajax call works: http://jsfiddle.net/vqut9zy2/1/. – Felix Kling Dec 11 '14 at 18:11
  • Indeed I've noticed that chaining off the all() return object waits properly. In fact, going one step further and putting another Promise.all() as the return of the first one works as I would expect: http://jsfiddle.net/vqut9zy2/2/ So the question becomes, what is the difference between the promises in use here, and why are they behaving so almost-but-not-totally-right together? – Carl Dec 11 '14 at 18:19
  • Hm, that double-post was indeed not the best you could. It probably would have been more appropriate if you had [edited the other question](http://stackoverflow.com/posts/27415023/revisions) to make it more generic. – Bergi Dec 11 '14 at 18:25
  • Apologies. If you need to kill one of them, I'd suggest the other, since it's not really specific to Ember's RSVP.all, but rather promises vs jquery deferred. – Carl Dec 11 '14 at 18:28

2 Answers2

2

you need to convert jQuery's deferred to a promise first.

function delayAjax(delay) {
    return Promise.resolve($.ajax({
        url: '/echo/json/',
        data: {
            json: '',
            delay: delay,
        }
    }));
}

http://jsfiddle.net/evilbuck/vqut9zy2/3/

Evil Buck
  • 1,068
  • 7
  • 20
  • 1
    I'm happy to accept this as the answer, since it functionally gets me where I want to go. Looking into the code a little, it seems that the check on the items in the list is only that they are "thenable". Can anyone explain why these appear to be compatible but behave subtly differently when chained? – Carl Dec 11 '14 at 18:22
  • Just so I understand, this is a work-around because of some problem with jQuery's promises? It would not be needed if jQuery promises behaved properly? – jfriend00 Dec 11 '14 at 18:54
  • I don't think that's true precisely. jQuery is using their own "deferred" object, which looks and feels similar, but is not truly a promise. The fact that it is thenable makes it look like it can be used in conjunction with promises, but there are apparently some gotchas like this. I would wager that jquery does not consider this a bug. Their objects work in conjunction with each other just fine. – Carl Dec 11 '14 at 19:18
0

There is a section in You Don't Know JS: Async & Performance that discusses how to manage untrustable thenables: Trustable Promise?.

It does indeed give Promise.resolve(thenable) as the way to approach this.

I don't know enough to explain why you were witnessing the behavior you were, but Domenic Denicola's article You're Missing the Point of Promises goes into some of the reasons why jQuery's deferreds are not genuine promises, and why they are problematic. At the end of the article, he provides the way to handle untrustable promises in the Q library, which is to use Q.when() (the Angularjs $q service also provides a .when() method for the same purpose):

return Q.when($.ajax({
    url: '/echo/json/',
    data: {
        json: '',
        delay: delay,
    }
}));
JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • I appreciate the extended reading on the subject. For my purposes, I've used Ember.RSVP.Promise to do this, just to keep it all nepotistic. – Carl Dec 11 '14 at 18:41