0

using jQuery 3.2.1 I'm chaining calls to $.ajax like this:

function get(i) {
    return $.ajax(...)
      .done(function() { console.log('get done: ' + i) })
      .fail(function() { console.log('get failed: ' + i) })
}

var p = Promise.resolve();
for (var i = 0; i < len; i++) {
    p = p.then(get(i));
}

p.done(function() { console.log('Finished') });

and I would expect that the ajax call for i=1 would not execute until the call for i=0 had resolved. similarly, the final done() should wait for all the calls to be performed in order

in reality I'm seeing 'Finished' before I see anything else and the 'get done' are coming back in random order (the first one finished comes back first is my guess).

I'm used to Bluebird where this would work. what am I doing wrong?

* Addendum I *

to add to the above, I'm loading javascript files that have dependencies and thus need to be loaded in order. therefore, the first must complete before the second fetch is initiated, otherwise dependencies will fail

ekkis
  • 9,804
  • 13
  • 55
  • 105
  • I did come across this: http://stackoverflow.com/questions/16384841/chain-ajax-and-execute-it-in-sequence-jquery-deferred but there are no clear answers – ekkis Apr 01 '17 at 20:16
  • If you want to 'synchronize' the ajax calls you should nest them or follow the answer of the question you linked, setting them as callbacks. – Dez Apr 01 '17 at 20:32
  • @Dez, but I am nesting them. the `p = p.then(...)` nests them no? – ekkis Apr 01 '17 at 20:35
  • `p`, being a proper `Promise` instead of a jQuery deferred, has no `done` method – Bergi Apr 01 '17 at 20:39
  • 1
    You need to pass a callback *function* to `then`, not the promise that is the result of the `get(i)` *call* – Bergi Apr 01 '17 at 20:42

1 Answers1

2

There are two issues:

  • The get function is executed immediately, while the then method should get a reference to that function for later execution by the Promise implementation. So replace:

    p = p.then(get(i));
    

    with:

    p = p.then(get.bind(null,i));
    
  • JavaScript's native promises do not expose the done method, so replace:

    p.done(function() { console.log('Finished') });
    

    with:

    p.then(function() { console.log('Finished') });
    

You should probably also add a catch call to the final promise.

Corrected version:

function get(i) {
    return $.ajax('https://jsonplaceholder.typicode.com/posts/'+(i+1))
      .done(function() { console.log('get done: ' + i) })
      .fail(function(err) { console.log('get failed: ' + i) })
}

var len = 4;
var p = Promise.resolve();
for (var i = 0; i < len; i++) {
    p = p.then(get.bind(null,i));
}

p.then(function() { console.log('Finished') })
 .catch(function () { console.log('Error occurred') });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
trincot
  • 317,000
  • 35
  • 244
  • 286
  • Yes, gives compact code. You could also do `p.then( _ => get(i) );` – trincot Apr 01 '17 at 22:50
  • Uhhh, jQuery promises do have a `.done()` method, though I would not recommend using it as it has non-standard behavior. – jfriend00 Apr 02 '17 at 00:35
  • @jfriend00, although jQuery deferreds have a `done()` method, *p* in this code) will always be a standard promise, since we start out with one, and `then` is guaranteed to return a standard promise, so *done* is not available. – trincot Apr 02 '17 at 03:20
  • I'm just reacting to this statement of yours "Promises do not expose the done method". It depends upon what type of promise it is. An ES6 standard promise will not - a jQuery promise will so that statement by itself is a bit misleading. I understand how the jQuery promises are wrapped here so there will not be a `.done()` method, but that is not what you say in the text of your answer. You state it like it's a statement about all promises. Just thought you could slightly improve your answer by fixing the wording there. – jfriend00 Apr 02 '17 at 03:50
  • Updated that to avoid misinterpretation. – trincot Apr 02 '17 at 03:59