티스토리 뷰

 

During my recent work on an AngularJS project, I faced a peculiar issue. I needed to fetch data from multiple URLs, process the results once they were all fetched, and then update my controller's scope with the processed data. However, due to JavaScript's asynchronous nature, I found my view not getting updated promptly, leading to inconsistencies in the user experience. Here's how I resolved this issue using `Promise.all()` and `$scope.$applyAsync()`.

Issue

In my AngularJS application, I had to fetch data from a series of URLs. Each of these requests returned an object that I needed to process and then push into an array in my controller's scope. The array was then displayed in the application's view. The code for fetching and processing the data was something like this:

$scope.getData = function () {
    $http.get('url')
        .then(function (response) {
            const responseData = response.data;
            const dataPromises = responseData.map(function (item) {
                return $http.get('item-url')
                    .then(function (response) {
                        let itemData = response.data;
                        $scope.myArray.push(itemData);
                    });
            });

            //...
        });
};

 

The problem was that the view wasn't getting updated immediately when `$scope.myArray` was modified. This was because the `$http.get()` calls were asynchronous, and the view was getting rendered before the data was fully fetched and processed.


Solution 1. Promise.all()

Promise.all() is a built-in JavaScript function that takes an array of promises and returns a new promise that only resolves when all the promises in the array have resolved. This function was exactly what I needed to ensure that my view only got updated after all the data had been fetched and processed. I modified my code to use `Promise.all()` like this:

$scope.getData = function () {
    $http.get('url')
        .then(function (response) {
            const responseData = response.data;
            const dataPromises = responseData.map(function (item) {
                return $http.get('item-url')
                    .then(function (response) {
                        let itemData = response.data;
                        return itemData;
                    });
            });

            Promise.all(dataPromises).then(function (allData) {
                $scope.myArray = allData;
                //...
            });
        });
};

 

This solved part of the problem, but the view was still not getting updated immediately. This was because AngularJS wasn't aware that `$scope.myArray` was being modified inside the `Promise.all()` callback.


Solution 2. $scope.$applyAsync()

In AngularJS, `$scope.$apply()` is used to manually trigger a digest cycle, which checks for changes in the model and updates the view. But calling `$scope.$apply()` multiple times during the same digest cycle can lead to an error. `$scope.$applyAsync()`, on the other hand, schedules an apply operation that will occur later, which won't cause errors if it's called multiple times in the same digest cycle.

I modified my code to use `$scope.$applyAsync()` inside the `Promise.all()` callback, ensuring that AngularJS would be aware of the changes to `$scope.myArray` and update the view accordingly:

$scope.getData = function () {
    $http.get('url')
        .then(function (response) {
            const responseData = response.data;
            const dataPromises = responseData.map(function (item) {
                return $http.get('item-url')
                    .then(function (response) {
                        let itemData = response.data;
                        return itemData;
                    });
            });

            Promise.all(dataPromises).then(function (allData) {
                $scope.myArray = allData;
                $scope.$applyAsync();
            });
        });
};

 

And there you have it! By combining `Promise.all()` and `$scope.$applyAsync()`, I was able to fetch and process multiple sets of data asynchronously, update my controller's scope when all the data was ready, and ensure that the view was updated promptly and correctly.

This approach demonstrates the power of JavaScript's Promise API and AngularJS's built-in services and tools. It allows us to manage complex asynchronous operations more easily and efficiently.

 

Happy Coding!