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...

5
  • 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? Commented Mar 21, 2017 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. Commented Mar 21, 2017 at 18:07
  • why? there is no idea to get responses in order? @JBNiz Commented Mar 21, 2017 at 18:51
  • i want them to be sequential. there is no way to get response in sequential? @NikolajDamLarsen Commented Mar 21, 2017 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. Commented Mar 21, 2017 at 19:00

2 Answers 2

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
        });
}
Sign up to request clarification or add additional context in comments.

2 Comments

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.
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.
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 
    });
}

14 Comments

I'm sorry, but your code isn't logically any different than the code in the question.
why not since http is asynchronous it for loop does't wait until the response. It keeps on executing and that's why it should always send last item of the array
This doesn't change that at all.
why not could you be more specific
Just moving the call to $http to a separate function, doesn't make it wait. This executes exactly as the code in the question.
|

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.