1

I'm working on an Angular.js app which makes some calls to the GitHub API. First, a call is made to retrieve all the repos for a user. Then for each repo, a call is made to retrieve the README. I have a javascript function which I would like to be run only after all the README API calls are completed.

Here is my controller:

readmeSearch.controller('ReadMeSearchController', ['RepoSearch', 'ReadMeSearch', function(RepoSearch, ReadMeSearch) {

  var self = this;
  self.gitRepoNames = [];
  self.readMes = [];
  self.noReadMes = [];

  self.doSearch = function() {
    RepoSearch.query(self.username)
      .then(function(repoResponse) {
        addRepoNames(repoResponse);
        for(var i = 0; i< self.gitRepoNames.length; i++) {
          (function(i) {
            ReadMeSearch.query(self.username, self.gitRepoNames[i])
              .then(function(readMeResponse) {
                addToReposWithReadMes(readMeResponse, i);
              }).catch(function(e){
                addToReposWithoutReadMes(repoResponse, i);
              });
          })(i);
        };
      });
  };

  addRepoNames = function(response) {
    self.searchResult = response.data;
    for(var i = 0; i < self.searchResult.length; i++) {
      var name = self.searchResult[i]['name']
      self.gitRepoNames.push(name);
    };
  };

  addToReposWithReadMes = function(response, i) {
    self.readMes.push(
      {
        name: self.gitRepoNames[i],
        size: parseInt(response.data["size"]),
        url: response.data["html_url"]
      }
    );
  };

  addToReposWithoutReadMes = function(response, i) {
    self.noReadMes.push(
      {
        name: self.gitRepoNames[i]
      }
    );
  };

  percentageOfReposWithReadMes = function() {
    var percentage;
    percentage = (self.noReadMes.length / self.gitRepoNames.length) * 100
    self.readMePercentage = percentage.toFixed(1);
  };

}]);

The README API calls using the ReadMeSearch factory populate two arrays, one for repos with READMEs and one for repos without READMEs. I would like to run the function percentageOfReposWithReadMes only after the ReadMeSearch.query has been completed for all the repos in self.gitRepoNames.

I've tried using .then after the RepoSearch.query but this doesn't seem to work. I think I'm a bit muddled in my understanding of Angular promises and the .then function. Any help would be greatly appreciated.

dbatten
  • 437
  • 5
  • 18
  • So, everything after `RepoSearch.query(self.username)` doesn't work? Cause you have a `.then` there. EDIT: oh nevermind, I understand what you meant now, looking into it – sch Nov 27 '15 at 09:36
  • It all works fine I'm just trying to run the function `percentageOfReposWithReadMes` but this can only be run after the both the `self.noReadMes` and `self.gitRepoNames` have been populated i.e after the all the ReadMeSeach queries in the for loop have been completed. @klskl. EDIT: ah just read your edit @klskl, sorry if it wasn't clear – dbatten Nov 27 '15 at 09:41
  • @dbatten, you can either count number of requests finished and to check if **all** of the are proceeded or to use `$q.all` passing all promises to it and using `.then()` funciton as a result of when all promises **are resolved**. – WhiteAngel Nov 27 '15 at 10:01

3 Answers3

0

You could try using $q.all, this will reslove an array of promises, and .then after all these are done, you would run your function.

I'm having trouble trying this myself, would be easier if you had a plunkr or something.

I did find this thread that explains this pretty good, maybe you can find your answer there.

Community
  • 1
  • 1
sch
  • 1,368
  • 3
  • 19
  • 35
0

I think you have to create two defers and resolve then when the loop finish. With $q.all you can wait for till both refers finish and call the percentageOfReposWithReadMes function.

  readmeSearch.controller('ReadMeSearchController', ['RepoSearch', 'ReadMeSearch', '$q', function(RepoSearch, ReadMeSearch, $q) {

  var self = this;
  var gitRepoNameDefer;
  var noReadMesDefer;
  self.gitRepoNames = [];
  self.readMes = [];
  self.noReadMes = [];


  self.doSearch = function() {
    RepoSearch.query(self.username)
      .then(function(repoResponse) {
        gitRepoNameDefer = $q.defer();
        noReadMesDefer = $q.defer();
        addRepoNames(repoResponse);
        for(var i = 0; i< self.gitRepoNames.length; i++) {
          (function(i) {
            ReadMeSearch.query(self.username, self.gitRepoNames[i])
              .then(function(readMeResponse) {
                addToReposWithReadMes(readMeResponse, i);
              }).catch(function(e){
                addToReposWithoutReadMes(repoResponse, i);
              }).finally(function () {
                 finishDefers(i, self.gitRepoNames.length - 1);
              });
          })(i);
        }
        $q.all([gitRepoNameDefer, noReadMesDefer ]).then(fucntion () {
          percentageOfReposWithReadMes();
        });
      });
  };

  addRepoNames = function(response) {
    self.searchResult = response.data;
    for(var i = 0; i < self.searchResult.length; i++) {
      var name = self.searchResult[i]['name']
      self.gitRepoNames.push(name);

    };
  };

  addToReposWithReadMes = function(response, i) {
    self.readMes.push(
      {
        name: self.gitRepoNames[i],
        size: parseInt(response.data["size"]),
        url: response.data["html_url"]
      }
    );

  };

  addToReposWithoutReadMes = function(response, i) {
    self.noReadMes.push(
      {
        name: self.gitRepoNames[i]
      }
    );

  };

 function finishDefers (index, limit) {
   if (index === limit) {
      gitRepoNameDefer.resolve(self.readMes);
      noReadMesDefer.resolve(self.noReadMes);
   }
 }

 function percentageOfReposWithReadMes () {
    var percentage;
    percentage = (self.noReadMes.length / self.gitRepoNames.length) * 100
    self.readMePercentage = percentage.toFixed(1);
  };

}]);
Raulucco
  • 3,406
  • 1
  • 21
  • 27
  • hi @raulucco thanks for your response. I get a console error `ReadMeSearch.query(...).then(...).catch(...).always is not a function` when trying to run this – dbatten Nov 27 '15 at 10:30
  • I'm getting `ReadMeSearch.query(...).then(...).catch(...).done is not a function` now – dbatten Nov 27 '15 at 10:36
  • ah yes finally is the one, although it still seems to be running before the loop is finished? @raulucco – dbatten Nov 27 '15 at 10:41
0

Return your promises and chain from those promises

 self.doSearch = function() {
    var namesPromise = 
        RepoSearch.query(self.username)
            .then(function(repoResponse) {
                 addRepoNames(repoResponse);
                 //return promises for chaining
                 return lookupNamesPromises(repoResponse);
            }) .catch (function (error) {
                 //log error
            });
    //chain from promise
    namesPromise.then (function(promises) {
        $q.all(promises).finally( function() {
            percentageOfReposWithReadMes();
        })
    });
    //return promise for chaining elsewhere
    return namesPromise;
  };

Lookup function returns an array of promise.

  self.lookupNamesPromises = function (repoResponse) {
    var namesPromises = [];
    for(var i = 0; i< self.gitRepoNames.length; i++) {
          //begin IIFE closure
          (function(i) {
              var p = (ReadMeSearch.query(self.username, self.gitRepoNames[i])
                       .then(function(readMeResponse) {
                           addToReposWithReadMes(readMeResponse, i);
                           return readMeResponse;
                       }).catch(function(e){
                           addToReposWithoutReadMes(e, i);
                           return e;
                       })
              );
              namesPromises.push(p);
          })(i);
          //end IIFE closure
    }
           //return promises for chaining
    return namesPromises;
  };

By chaining your promises, subsequent functions will wait for previous functions to complete. The rule of thumb in functional programming is always return something.

georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • 1
    hi @georgeawg many thanks for your response. that seems to be working all fine, I think I was a bit muddled in my understanding of promises and $q but your answer has really cleared it up. thanks – dbatten Nov 30 '15 at 15:20