1

I'm using angular 1.4.1

  1. I use two webservices from a server ( i cannot edit them )

    • /institutions who return me a list of institutions with ids
    • /institution/XXX/collections/ ( with XXX is a institution id) who return me the collections of the current institution
  2. I create a service function (getInstitutions()) who use this webservices to get all the data about institutions and there sub collections :

     var singleton_institutions = null ; 
     this.getInstitutions = function(){
           var getHrefCollections = function(links){
             for(var l in links){ if(links[l]["rel"] == "collections"){ return links[l]["href"]; }}
             return null ;
          };
          if(singleton_institutions != null){
             // don't reload the data if already loaded
             return $timeout( function(){
                return singleton_institutions ;
             }, 0);
          }else {
             var institutions = [];
             var url = $rootScope.api + "institutions";
             return $http({
                method: 'GET',
                url: url
             }).then(
                 // we have the institutions list, now get each sub collections
                 function successCallback(response) {
                    institutions = response.data;
                    var promises = [];
                    for (var i in response.data) {
                       var url = getHrefCollections(response.data[i]["links"]);
                       promises.push( //Push the promises into an array
                           $http({
                              method: 'GET',
                              url: url
                           }).then(function successCallback(response) {
                               return response.data;
                           })
                       );
                    }
                    return $q.all(promises); // Resolve all promises before going to the next .then
                 },
                 function errorCallback(response) {
                    console.log(response);
                 }
             ).then(
                 function (resultArray) {
                    // formating the data
                    for (var i in resultArray) {
                       institutions[i]["collections"] = resultArray[i];
                       institutions[i]["institutionname"] = tools_getInstitutionName(institutions[i]["institutioncode"]);
                    }
                    singleton_institutions = institutions;
                    return singleton_institutions ;
                 },
                 function (error) {
                    console.log(error);
                 }
             );
          }
       };
    
  3. The problem :

In my service function i make a chained promise with $q, this work good, it take few seconds because it launch approximatly 30 https call (one per institution).

In the application initialization, this function are called twice in approximatly the same time. So the first call don't have time to finish and to init singleton_institutions before the second call so i have 60 http's call instead of half.

//call from a controller A
services.getInstitutions().then(function (institutions) {...}); // take few seconds to load the datas
//call from a controller B in near the same time
services.getInstitutions().then(function (institutions) {...}); // so this will make all the data load again because the first call is not finished
  1. QUESTION :

How to prevent services.getInstitutions() to get the data if another call is not finished yet.

AlainIb
  • 4,544
  • 4
  • 38
  • 64

2 Answers2

1

Thank to @Thalaivar link https://stackoverflow.com/a/21056378/337128 i make it work.

angular.module('interfaceApp').service('services',[ '$rootScope','$timeout','$http','$q',
   function($rootScope,$timeout,$http,$q ) {


     var vm = this ;
     // if institution is loading
     vm.institutionLoading = false ;
     // the trick is her, return the reference of the loading promise if loading in progress
     vm.deferpromise = null ;
     vm.getInstitutions = function(forceReload){
          var defer = $q.defer();
          if (!tools_defined(forceReload)) {
             forceReload = false
          }
          var getHrefCollections = function(links){
             for(var l in links){
                if(links[l]["rel"] == "collections"){
                   return links[l]["href"];
                }
             }
             return null ;
          };
          if( (!forceReload ) &&  (singleton_institutions != null)){
             // si la liste est déja chargé ne recharge pas
             return $timeout( function(){
                return singleton_institutions ;
             }, 0);
          }else {
             if(vm.institutionLoading) {
                return vm.deferpromise;
             }else{
                vm.deferpromise = defer.promise ;
                vm.institutionLoading = true;
                var institutions = [];
                var url = $rootScope.api + "institutions";
                $http({
                   method: 'GET',
                   url: url
                }).then(
                    // on a récupéré toutes les institutions, on récupére les données sur les collections
                    function success(response) {
                       institutions = response.data;
                       var promises = [];
                       for (var i in response.data) {
                          var url = getHrefCollections(response.data[i]["links"]);
                          promises.push( //Push the promises into an array
                              $http({
                                 method: 'GET',
                                 url: url
                              }).then(function success(response) {
                                 return response.data;
                              })
                          );
                       }
                       return $q.all(promises); // Resolve all promises before going to the next .then
                    },
                    function error(response) {
                       console.log(response);
                    }
                ).then(
                    function (resultArray) {
                       // il faut reconstruire les institutions correctement
                       // resultArray contient la liste des collections par institutions, dans le même ordre que institutions. on les fusionne
                       for (var i in resultArray) {
                          institutions[i]["collections"] = resultArray[i];
                          institutions[i]["institutionname"] = tools_getInstitutionName(institutions[i]["institutioncode"]);
                       }
                       singleton_institutions = institutions;
                       vm.institutionLoading = false;
                       defer.resolve(singleton_institutions);
                    },
                    function (error) {
                       console.log(error);
                    }
                );
                return defer.promise;
             }

          }
       };
});

So now i can do this and data get loaded only once :

        services.getInstitutions().then(function (allInstitutions) {
                console.log(allInstitutions.length);
        });
        services.getInstitutions().then(function (allInstitutions) {
                console.log(allInstitutions.length);
        });
        services.getInstitutions().then(function (allInstitutions) {
                console.log(allInstitutions.length);
        });
        $timeout(
            services.getInstitutions().then(function (allInstitutions) {
                    console.log(allInstitutions.length);
            }), 15000
        );
        $timeout(
            services.getInstitutions().then(function (allInstitutions) {
                    console.log("last " + allInstitutions.length);
            }), 25000
        );
Community
  • 1
  • 1
AlainIb
  • 4,544
  • 4
  • 38
  • 64
0

Have a look at async library. You can execute your functions in parallel or series and your callback will be executed once all of them are finished.

https://github.com/caolan/async

async.parallel([
    function(){ ... },
    function(){ ... }
], callback);

async.series([
    function(){ ... },
    function(){ ... }
]);

If you want to go with angular way, why not use $q.all. Here is a neat example which you can re-use with what you are doing.

http://jsfiddle.net/jsengel/mc3p01nb/

Thalaivar
  • 23,282
  • 5
  • 60
  • 71
  • Thanks for the help. The problem is the two `services.getInstitutions()` calls are not made in the same controllers so i don't think async will be usefull here. For the jsfiddle that's what i actullay doing. I want to prevent `services.getInstitutions()` to get the data if another call is not finished yet. I will read about $q – AlainIb May 10 '16 at 09:26
  • @AlainIb: $q.all would work... in your situations, why not move the api calls to resolve part of your routes... – Thalaivar May 10 '16 at 09:28
  • What did you mean please, i'm not so familiar with $q – AlainIb May 10 '16 at 09:29
  • @AlainIb: did you look at the fiddle i shared with on $q.all... will that help? – Thalaivar May 10 '16 at 09:31
  • `$q.all` would not work, all promises will be executed in parallel, all `$q.all` does is wait until all its passed promises have resolved. It will wrap non promise objects, it doesn't execute anything in series. – ste2425 May 10 '16 at 09:31
  • @Thalaivar yes. i already made this `return $q.all(promises);` – AlainIb May 10 '16 at 09:31
  • @ste2425 did you have an idea please ? – AlainIb May 10 '16 at 09:33
  • @AlainIb: http://stackoverflow.com/a/21056378/337128 will this work for you, where you call the function only after all the promises are returned. – Thalaivar May 10 '16 at 09:55
  • @AlainIb I don't have much time spare, working. But other people in this situation create a solution similar to `$q.all` except it will execute in series rather than parallel. The downside is you need to give it an array of factory methods which it will execute to receive the promise. Thats how it can make the promise execution wait. I created an [example](https://jsfiddle.net/1d3zcwvp/) You would have to integrate it but you get the idea. Ignore my half ES6 syntax, still getting used to it :) – ste2425 May 10 '16 at 09:58