1

First, I am sorry for my bad English. I am new in angular js. I am facing a problem with my api call inside loop. This is my code demo.

    $http
    .get("http://localhost:8000/api/product-track/get-product-phase-item/" + item_id, {
        transformRequest: angular.identity,
        headers: {'Content-Type': undefined, 'Process-Data': false}
    })
    .then(function(response){
        data = response.data.item_category;
        $scope.items = response.data.data;

        angular.forEach($scope.items, function(item){

            $http
                .get("http://localhost:8000/api/item/get-item-name/" + item.item_category_id, {
                    transformRequest: angular.identity,
                    headers: {'Content-Type': undefined, 'Process-Data': false}
                })
                .success(function (response) {
                    data = response;
                    console.log(data);

                });

        });


    });

Now i explain what problem i am faced with that code. My first api call perfectly. When my second api call inside loop it also execute perfectly. But when the value of item.item_category_id is same, i am facing problem. Then my api does not call sequentially. I don't know, have i explained my problem correctly.

I am given an example. When my loop execute 5 times and the urls are-

  1. http://localhost:8000/api/item/get-item-name/2"
  2. http://localhost:8000/api/item/get-item-name/4"
  3. localhost:8000/api/item/get-item-name/5"
  4. localhost:8000/api/item/get-item-name/4"
  5. localhost:8000/api/item/get-item-name/4"
  6. localhost:8000/api/item/get-item-name/6"

Then i got response first no 1 then 2 then 3 then 6 then 4 then 5. When the id is repeated it does not response sequentially.Why i am faced this problem. Thanks...

Mahfuz Shishir
  • 841
  • 1
  • 7
  • 24
  • 2
    Because all the requests are sent at the same time, and the 6th one took less time to precess by the server/network than the 4th and 5th one. You should not expect the responses to come back in the same order. If you need that, use $q.all(). I also don't uderstand why all the 6 response handlers all woverwrite the same data variable. Why send 6 requests in parallel if, in the end, you only keep the result of one of them? – JB Nizet Mar 21 '17 at 18:02
  • Why do you want them to be sequential? As I see it, whichever API call finishes last, overrides what all the others have returned, so you could (as your code is now) get away with only calling a single of those api endpoints for a similar result, the rest is doing nothing but wasting bandwidth. – Nikolaj Dam Larsen Mar 21 '17 at 18:07
  • why? there is no idea to get responses in order? @JBNiz – Mahfuz Shishir Mar 21 '17 at 18:51
  • i want them to be sequential. there is no way to get response in sequential? @NikolajDamLarsen – Mahfuz Shishir Mar 21 '17 at 18:52
  • 1
    You can chain the promises like in Sachet Gupta's answer, but can you explain why you want them in order? Remember, if every request takes say 1 second to return, and you have 10 items in your list, your users would then have to wait 10 seconds for the last data to be ready. Whereas, if made in parallel, the last result would be ready after ~1 second (given the same conditions as above). The only reason why you would need these to be sequential, is if each call were dependent on the data returned by the last call. But base on the code you've shared so far, that's not the case at all. – Nikolaj Dam Larsen Mar 21 '17 at 19:00

2 Answers2

3

Explicitly synchronize the calls. Use recursion to fetch the item from the success call back of the previous item.

Limitation: If any of the call fails while fetching item details, it will also block the calls to fetch the subsequent ones.

To fix this limitation, you can place the call for next item from error callback of the previous item as well.

Example below:

$http
    .get("http://localhost:8000/api/product-track/get-product-phase-item/" + item_id, {
        transformRequest: angular.identity,
        headers: {
            'Content-Type': undefined,
            'Process-Data': false
        }
    })
    .then(function(response) {
        data = response.data.item_category;
        $scope.items = response.data.data;
        fetchItemsDetail(0); // start with 1st element (item)
    });

function fetchItemsDetail(itemIndex) {
    $http
        .get("http://localhost:8000/api/item/get-item-name/" + $scope.items[itemIndex].item_category_id, {
            transformRequest: angular.identity,
            headers: {
                'Content-Type': undefined,
                'Process-Data': false
            }
        })
        .success(function(response) {
            data = response;
            console.log(data);
            if ($scope.items.length - 1 > itemIndex)
                fetchItemsDetail(itemIndex + 1); // place call to fetch next item details
        })
        .error(function(error) {
            console.log(error);
            if ($scope.items.length - 1 > itemIndex)
                fetchItemsDetail(itemIndex + 1); // place call to fetch next item details even if this one fails
        });
}
Sachet Gupta
  • 822
  • 5
  • 18
  • While this does what he wants, you could argue that what he want isn't really a great idea. Especially if that item list is long. Also, his data is still overwritten with each new request, so only the last request will ever matter - unless the requests has side effects server-side that we don't know of. – Nikolaj Dam Larsen Mar 21 '17 at 18:30
  • 1
    yes, though @Mahfuz Shishir has a requirement which is bit out of the box and will have a big hit on the performance, but yes this solution can work for him. – Sachet Gupta Mar 22 '17 at 03:36
0

abstract the http call from for loop and put it in a separate function. Then call that function inside foreach.

Reason is that item in foreach reference the same memory even though it create a new closure.That's why for each call it refer same value

$http
    .get("http://localhost:8000/api/product-track/get-product-phase-item/" + item_id, {
        transformRequest: angular.identity,
        headers: {
            'Content-Type': undefined,
            'Process-Data': false
        }
    })
    .then(function(response) {
        data = response.data.item_category;
        $scope.items = response.data.data;
        angular.forEach($scope.items, function(item) {
            sendReq(item)
        });
    });

function sendReq(item) {
    $http
        .get("http://localhost:8000/api/item/get-item-name/" + item.item_category_id, {
            transformRequest: angular.identity,
            headers: {
                'Content-Type': undefined,
                'Process-Data': false
            }
        })
        .success(function(response) {
            data = response;
            console.log(data);
        });
}

Or if you want to send the all the requests at one shot then use the $q.all

$http
    .get("http://localhost:8000/api/product-track/get-product-phase-item/" + item_id, {
        transformRequest: angular.identity,
        headers: {
            'Content-Type': undefined,
            'Process-Data': false
        }
    })
    .then(function(response) {
        data = response.data.item_category;
        $scope.items = response.data.data;
        $scope.fullArr = []
        angular.forEach($scope.items, function(item) {
            $scope.fullArr.push($http
                .get("http://localhost:8000/api/item/get-item-name/" + item.item_category_id, {
                    transformRequest: angular.identity,
                    headers: {
                        'Content-Type': undefined,
                        'Process-Data': false
                    }
                }));
        });
        sendReq()
    });

function sendReq() {
    $q.all(fullArr).then(function(response) {
        //response for all requests 
    });
}
Sachila Ranawaka
  • 39,756
  • 7
  • 56
  • 80