2

I have angular js application. where I am trying to parse a json array using angular.forEach. It is showing strange behavior.

When I am trying to console this it is showing data but when I trying to console with length it is showing length as 0.

I want output outside the loop only. How can I achieve this ?

Can anyone help me on this ?

function loadRelease() {
    $scope.datas.releaseData = [];
    angular.forEach($scope.datas.repositoryData, function(value, key) {
        GitHubService.getDevMasterReleaseDate(value.url)
            .then(function(responseRepo) {
                var dataToOperate = [];
                var dataJson = {
                    'repoName': value.name
                    , 'masterArray': []
                    , 'devArray': []
                }

                angular.forEach(responseRepo.data, function(value, key) {
                    if (value.target_commitish == 'master') {
                        dataJson.masterArray.push(value);
                    } else {
                        dataJson.devArray.push(value);
                    }
                });

                $scope.datas.releaseData.push(dataJson);
            }, function(error) {

            });

    });
    console.log($scope.datas.releaseData);
    console.log('length :: ' + $scope.datas.releaseData.length);

}

Console:

enter image description here

1
  • 1
    move the consoles inside the then Commented Aug 7, 2018 at 5:57

4 Answers 4

1

Objects/Arrays are passed by reference and Primitive Values are passed by Values in JavaScript.

Here is the proper explanation of the anomaly (well not really):

function loadRelease() {
    $scope.datas.releaseData = []; // LINE A
    angular.forEach($scope.datas.repositoryData, function(value, key) {
    GitHubService.getDevMasterReleaseDate(value.url).then(function(responseRepo) {
            var dataToOperate = [];
            var dataJson = {
                'repoName' : value.name,
                'masterArray' : [],
                'devArray' : [] 
            }

            angular.forEach(responseRepo.data, function(value, key) {
                if(value.target_commitish == 'master') {
                    dataJson.masterArray.push(value);
                } else {
                    dataJson.devArray.push(value);
                }
            });

            $scope.datas.releaseData.push(dataJson); // LINE B
        }, function(error) {

        });

    });
    console.log($scope.datas.releaseData); // LINE C
    console.log('length :: ' + $scope.datas.releaseData.length); //LINE D

}

In your code what you are doing is console logging the length of an empty array initialised on LINE A (Marked in the code above). Length is a primitive value that is not passed as a reference rather by value, so any updation to that won't reflect in your console.log. But when you log your actual array on LINE C, it is passed by reference to console.log method and when the passed array gets updated after the promise of the async call of GitHubService.getDevMasterReleaseDate resolves, its value gets updated in the console.log method as well because it is passed reference and not value. If you want to get the expected behaviour you have to move your console logs inside the function passed in .then.

Sign up to request clarification or add additional context in comments.

Comments

0

Updated Solution:

You have to wait until all of the promises are resolved before trying to use the result. You can do this with Promise.all:

function loadRelease() {
    $scope.datas.releaseData = [];
    var promises = [];
    angular.forEach($scope.datas.repositoryData, function(value, key) {
        promises.push(GitHubService.getDevMasterReleaseDate(value.url).then(function(responseRepo) {
            var dataToOperate = [];
            var dataJson = {
                'repoName' : value.name,
                'masterArray' : [],
                'devArray' : [] 
            }

            angular.forEach(responseRepo.data, function(value, key) {
                if(value.target_commitish == 'master') {
                    dataJson.masterArray.push(value);
                } else {
                    dataJson.devArray.push(value);
                }
            });

            $scope.datas.releaseData.push(dataJson);
        }, function(error) {

        }));
    });
    Promise.all(promises).then(() => {
        console.log($scope.datas.releaseData);
        console.log('length :: ' + $scope.datas.releaseData.length);
    });
}

Previous Answer:

You are logging the results outside of an asynchronous call to the API.

Chrome console will actually update the logged value once the results come back from the async call (as you are logging out an object/array), which is why the first log has results. The second one, however, is a primitive so it stays as the true value at the time of logging, which is 0.

If you want the correct output, move your console.logs inside of the .then function.

GitHubService.getDevMasterReleaseDate(value.url).then(function(responseRepo) {
    ...
    $scope.datas.releaseData.push(dataJson);
    console.log($scope.datas.releaseData);
    console.log('length :: ' + $scope.datas.releaseData.length);
}, function(error) {

});

3 Comments

I want to get those value outside the loop. How can i do this ?
@SimonK The answer is good but overhead code. It can be done more simply.
OP doesn't want the value after each loop, only once all loops are complete. This is the simplest way
0

Yes, both of the above answers are correct. Your code is async. Your are logging on the console ( and using $scope.datas.releaseData before it is fetched from the API. ) If you really want to access the $scope.datas.releaseData outside the .then(), pass a function as a callback & then call this function, inside .then() function. Take idea from the below code :

var logResponse = function(){

  console.log($scope.datas.releaseData);
  console.log('length :: ' + $scope.datas.releaseData.length);

}

loadRelease(logResponse); //pass variable containing your function

function loadRelease(myCallBack) {
  $scope.datas.releaseData = [];
  angular.forEach($scope.datas.repositoryData, function(value, key) {
    GitHubService.getDevMasterReleaseDate(value.url)
      .then(function(responseRepo) {
        var dataToOperate = [];
        var dataJson = {
          'repoName': value.name
          , 'masterArray': []
          , 'devArray': []
        }

        angular.forEach(responseRepo.data, function(value, key) {
          if (value.target_commitish == 'master') {
            dataJson.masterArray.push(value);
          } else {
            dataJson.devArray.push(value);
          }
        });

        $scope.datas.releaseData.push(dataJson);
        myCallBack(); //called here
      }, function(error) {

      });

  });
}

NOTE : Use Promises for a professional manner, because Callbacks, if used excessively, cause Callback Hells. But, if you really want to understand the logic behind promises, you can use or understand Callbacks.

Comments

0

The reason is "Asynchronous Code" the value of $scope.datas.releaseData is set inside an Asynchronous Code/API/Ajax {GitHubService.getDevMasterReleaseDate}.

The code written for console on 2nd last line doesn't have value at the time of execution, but in chrome, if a printed object is expanded it shows the latest value of the object. For clarity try to replace the 2nd last line with console.log(JSON.stringify($scope.datas.releaseData))

But the console on the last line is an integer it shows the correct value of object length which was at the time of the execution.

If you want the value of the object outside the API

Create a function**(eg. processReleaseData())** and call it after the push code. Withing that function you will receive the value of the variable($scope.datas.releaseData)

function loadRelease() {
    $scope.datas.releaseData = [];
    angular.forEach($scope.datas.repositoryData, function(value, key) {
        GitHubService.getDevMasterReleaseDate(value.url)
            .then(function(responseRepo) {
                var dataToOperate = [];
                var dataJson = {
                    'repoName': value.name
                    , 'masterArray': []
                    , 'devArray': []
                }

                angular.forEach(responseRepo.data, function(value, key) {
                    if (value.target_commitish == 'master') {
                        dataJson.masterArray.push(value);
                    } else {
                        dataJson.devArray.push(value);
                    }
                });

                $scope.datas.releaseData.push(dataJson);
                processReleaseData($scope.datas.releaseData);
                //can call processReleaseData() directly also without 
                //passing arg
            }, function(error) {

            });
    });

    console.log($scope.datas.releaseData);
    console.log('length :: ' + $scope.datas.releaseData.length);
}

function processReleaseData(releaseData){
    //Do here whatever you want to do with releaseData;
    //You can also directly access the $scope.datas.releaseData here
}

6 Comments

I want to get those value outside the loop. How can i do this ?
@SangramBadi I have updated my answer, see now, how easily and simply you can achieve what you want.
i did this, it'll work inside loop if i put that function but i want that function outside the loop. As @Simon K answered to use promise it's working fime
@SangramBadi I updated the answer. Did you try doing it this way?
@Ronit, OP doesn't want to call a function for every iteration of the outer angular.forEach, they only want to call it once and only after all promises have been resolved.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.