0

I am trying to understand Promise in Jquery. In the below example i am trying to load a set of urls from an array and after its loaded, i want to append a text to the html

array1 = ["helloworld.html", "helloworld.html", "helloworld.html"]

loadData(array1).then(function() {
  $('pre').append("done");
});


function loadData(gim) {
    for (i = 0; i < gim.length; i++) {
      console.log(i);
      $.get(gim[i], function(data) {
        console.log("a = " + i);
        $('pre').append(data+" -- "+i+"<br>");
      });
    }
  }

but the text "done" is not appended. source code in http://plnkr.co/edit/EEaQqfnUyVHGZBgDCRPs?p=preview

Santhosh
  • 9,965
  • 20
  • 103
  • 243
  • You need to return promise from `loadData`. Please read the [`$q` docs](https://docs.angularjs.org/api/ng/service/$q) – Mosh Feu Mar 20 '16 at 11:32
  • can you explain it more – Santhosh Mar 20 '16 at 11:36
  • That's why there are docs and tutorials in the world ;) It's kind of basic,, A nice article about it you can find [here](http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/) Search for "Upgrading to promises". If there is something specific that's you don't understand I will glad to explain as much as I could. – Mosh Feu Mar 20 '16 at 11:49
  • can you explain for jquery not angular js – Santhosh Mar 20 '16 at 11:54

1 Answers1

1

I see three separate issues here.

  1. Your loop index is wrong when the ajax call finishes because the for loop has run its course before the async ajax call finishes.
  2. You will need to find a way to wait until the last async operation is done before appending the "done". You can properly use promises for that.
  3. Your ajax results will not necessarily be appended in order because it is indeterminate what order the ajax calls will finish in. Depending upon the server architecture and what is involved in the requests, they "may" finish in the order they are requested, but since you are running them all in parallel, that is certainly not guaranteed.

The first issue is because the operation is asynchronous and thus your loop index is at the final value by the time the async operation finishes. See this answer for a solution to that part of the problem. See that solution implemented with your code here: http://plnkr.co/edit/cUFGZL0Bm6unRca3X7N5?p=preview

If you want to know when all the ajax operation are done so you can append the "done", then you will want to properly use promises for that. You can do this:

  <script>
    array1 = ["helloworld.html", "helloworld.html", "helloworld.html"]

    loadData(array1).then(function() {
      $('pre').append("done");
    }, function() {
      // handle error here
    });


    function loadData(gim) {
        var promises = [];
        for (var i = 0; i < gim.length; i++) {
          (function(index) {
            console.log(index);
            promises.push($.get(gim[index]).then(function(data) {
              console.log("a = " + index);
              $('pre').append(data+" -- "+index+"<br>");
            }));
          })(i);
        }
        return Promise.all(promises);
      }
  </script>

Working example: http://plnkr.co/edit/KrC7Lxu9OHs9suvNOC8t?p=preview

Here you collect an array of promises that are returned by $.get() into an array and you use Promise.all() to tell you when all those promises are done.

FYI, this code does NOT guarantee that the results will be appended in order as the async operations could complete in any order.


If you want to run the operations in parallel, but make sure they are added in the requested order, then you can change your code like this to collect all the results, then add the results to the DOM in guaranteed order:

  <script>
    array1 = ["helloworld.html", "helloworld.html", "helloworld.html"]

    loadData(array1).then(function() {
      $('pre').append("done");
    }, function() {
      // handle error here
    });


    function loadData(gim) {
        var promises = [];
        for (var i = 0; i < gim.length; i++) {
          promises.push($.get(gim[i]));
        }
        return Promise.all(promises).then(function(results) {
          results.forEach(function(item, index) {
            $('pre').append(item + " -- "+index+"<br>");
          });
        });
      }
  </script>

Working example: http://plnkr.co/edit/nUHwFMg22V8rc9v12SqR?p=preview


If you want to use only jQuery promise functions (which are a bit non-standard), you can do this:

  <script>
    array1 = ["helloworld.html", "helloworld.html", "helloworld.html"]

    loadData(array1).then(function() {
      $('pre').append("done");
    }, function() {
      // handle error here
    });


    function loadData(gim) {
        var promises = [];
        for (var i = 0; i < gim.length; i++) {
          promises.push($.get(gim[i]));
        }
        return $.when.apply($, promises).then(function() {
          var results = Array.prototype.slice.call(arguments);
          console.log(results);
          results.forEach(function(item, index) {
            $('pre').append(item[0] + " -- "+index+"<br>");
          });
        });
      }
  </script>

Working example: http://plnkr.co/edit/IHMnEbYEFLjfguDkoubg?p=preview

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979