5

First of all, I am not good at angularjs.

While I've been studying about $q, I faced a weird problem.

When I use $q.all, I put $http in regular sequence expecting to get results in same order,

but what I get was random results.

See this and correct my stupidity.

    $q.all([
        HttpService.editItem(
            $scope.$parent.category_id,           //  category id
            Define.CAR_CAT,                         //  category url to request
            $scope.car_id,                           //  car_id wanna edit
            {car_name: inputValue.toUpperCase()}    //  data
        ),
        HttpService.getCarList(
            $scope.$parent.category_id,     //  category id
            Define.CAR_CAT                    //  category url to request
        )
    ]).then(function (results) {
        if (results[0].statusText === 'OK' && results[1].statusText === 'OK') {
            .....
    });

'HttpService' is a service of my app. It returns promise.

What I expected was

edit car name first, get car list later.

But results I got was get car list first, edit car name later.

And I'm using

return $q(function(resolve, reject){ });

instead of using

$q.defer();

.

.

.

.

and these are my HttpService part

function editItem(cat_id, cat_url, content_id, item_data) {
    return $q(function (resolve, reject) {
        $http({
            method: 'PUT',
            url: Define.TEST_URL + cat_id + cat_url + content_id,
            data: item_data
        }).then(function (response) {
            resolve(response);
        }, function (error) {
            reject(error);
        });
    });
}



function getCarList(cat_id, cat_url) {
    return $q(function (resolve, reject) {
        $http({
            method: 'GET',
            url: Define.TEST_URL + cat_id + cat_url
        }).then(function (response) {
            resolve(response);
        }, function (error) {
            reject(error);
        });
    });
}

and here is the getCarList response

{
    "error_msg": "",
    "error_num": 0,
    "statusText": "OK"
    "results": [
            {
            "car_id": "CAR0121",
                "car_name": "AUDI R8"
            },
            {
                "car_id": "CAR0122",
                "car_name": "AUDI A6"
            },
            {
                "car_id": "CAR0128",
                "car_name": "BENZ"
            },
            {
                "car_id": "CAR0130",
                "car_name": "PORCHE"
            },
    ]
}
Canet Robern
  • 1,049
  • 2
  • 11
  • 28

3 Answers3

7

Is there an method order in $q.all in Angularjs?

Yes, the Order is regards to Promises order you gave it to $q.all()

From ref: $q.all()

Returns a single promise that will be resolved with an array/hash of values, each value corresponding to the promise at the same index/key in the promises array/hash. If any of the promises is resolved with a rejection, this resulting promise will be rejected with the same rejection value.

Example 1 (list)

var  promises = [promise1(), promise2(), promise3()];

$q.all(promises).then((values) => {
    console.log(values[0]); // value promise1
    console.log(values[1]); // value promise2
    console.log(values[2]); // value promise3
});

Example 2 (map)

var  promises = {one: promise1(), two: promise2(), three: promise3()};

$q.all(promises).then((values) => {
    console.log(values.one); // value promise1
    console.log(values.two); // value promise2
    console.log(values.three); // value promise3
});

But results I got was get car list first, edit car name later.

I suggest you to create map approach and test what you get:

$q.all({edit:
    HttpService.editItem(
        $scope.$parent.category_id,           //  category id
        Define.CAR_CAT,                         //  category url to request
        $scope.car_id,                           //  car_id wanna edit
        {car_name: inputValue.toUpperCase()}    //  data
    ),
    getCar: HttpService.getCarList(
        $scope.$parent.category_id,     //  category id
        Define.CAR_CAT                    //  category url to request
    )
}).then(function (results) {
   // results.edit
   // results.getCar 
});

EDIT

demo Plunker using Map

demo Plunker using List

Maxim Shoustin
  • 77,483
  • 27
  • 203
  • 225
  • Well, in my code, http call is already in array of $q.all. If I follow your suggestion, calling function have to be in 'then' function of $q.all, right? – Canet Robern Sep 12 '17 at 00:11
  • 1
    updated answer, changed to built-in, see `$q.all({edit: .... getCar: })` – Maxim Shoustin Sep 12 '17 at 03:54
  • Result of this was same as original. I think $q.all does not guarantee order of array when http call is inside of function what is in $q.all array. – Canet Robern Sep 12 '17 at 05:51
  • 1
    @CanetRobern yes, it guarantees. Did you use map instead list? can u post demo in plunker that shows up your problem? – Maxim Shoustin Sep 12 '17 at 06:47
  • 1
    @CanetRobern take a look on this demo MAP: https://plnkr.co/edit/t9MHfCyDCBvQ0P7Cyq2d?p=preview and this demo as LIST: https://plnkr.co/edit/d8mXEAfVnonqh7xBO4Xa?p=preview – Maxim Shoustin Sep 12 '17 at 06:54
  • Now I used local server hosting, so cannot show you http response sequences using plunker.. I copied your code and pasted it but using map does not change my result. Maybe $q.all can only handle results of its own arrays, not handle orders of http calls. – Canet Robern Sep 12 '17 at 07:00
  • $timeout function, yes it shows results same as $q.all() array sequences. But http call, I guess, this can be happened because of each request time or response time make very tiny gap. – Canet Robern Sep 12 '17 at 07:06
  • 1
    @CanetRobern not at all. $timeout and $http both return Promise. I think something wrong with your service logic. – Maxim Shoustin Sep 12 '17 at 07:56
  • I added my HttpService codes and response of getCarList. Please see this more. – Canet Robern Sep 12 '17 at 09:21
  • 1
    @CanetRobern question: do you want 1st call `editItem` and after get updated `getCarList` ? – Maxim Shoustin Sep 12 '17 at 09:33
  • Yes! Of course! Did my bad English skill make you confused? T.T – Canet Robern Sep 12 '17 at 09:40
  • 1
    @CanetRobern OMG, for sure you cannot use `q.all` because in this case angular send both requests at the same time. Only Promise chain will help you – Maxim Shoustin Sep 12 '17 at 09:50
  • Well then, $q.all cannot handle multiple http calls as expected.. Thanks for your interest! It was big helpful for my angular skill :) – Canet Robern Sep 13 '17 at 00:11
4

If you want the calls to be sequential, you'll need to call them by using promise chaining instead of $q.all

HttpService.editItem(
        $scope.$parent.category_id,             //  category id
        Define.CAR_CAT,                         //  category url to request
        $scope.car_id,                          //  car_id wanna edit
        {car_name: inputValue.toUpperCase()}    //  data
    )
    .then(function(result) {
        if (result.statusText === 'OK') {
            return HttpService.getCarList(
                $scope.$parent.category_id,     //  category id
                Define.CAR_CAT                  //  category url to request
            )
        }
        else {
            return $q.reject();
        }
    })
    .then(function (result) {
        if (result.statusText === 'OK') {
        .....
    });
Canet Robern
  • 1,049
  • 2
  • 11
  • 28
Icycool
  • 7,099
  • 1
  • 25
  • 33
3

but what I get was random results.

$q.all will await for promises running concurrently. So there is no guarantee which request hits server first. But the order of responses will be kept.

If you want to make sure you read after write you have to wait for write response to respond it was ok

var editing = HttpService.editItem(
        $scope.$parent.category_id,           //  category id
        Define.CAR_CAT,                         //  category url to request
        $scope.car_id,                           //  car_id wanna edit
        {car_name: inputValue.toUpperCase()}    //  data
    )
 var reading = editing.then(function() {
    return HttpService.getCarList(
        $scope.$parent.category_id,     //  category id
        Define.CAR_CAT                    //  category url to request
    )
   })

//if you need both results
$q.all([editing, reading]).then(function(results) {

})
Yury Tarabanko
  • 44,270
  • 9
  • 84
  • 98
  • I think your suggestion is a kind of nice way, but I don't want to seperate this codes.. T.T But, thanks to your guide – Canet Robern Sep 11 '17 at 09:48
  • 1
    @CanetRobern Then you can't be sure which request will reach your server first. Thus sometime you will experience "dirty reads" – Yury Tarabanko Sep 11 '17 at 09:49
  • I executed my app following your code, but still got same result.. getCar first, edit later. I think if function in $q.all array has http call, this does not guarantee http call responses order same as order of $q.all array – Canet Robern Sep 12 '17 at 06:05
  • 1
    @CanetRobern What you are saying is just impossible with the code I have posted. First of all `$q.all` knows nothing about the nature of the promises it uses (so it can't affect the order of requests even in principle). Second `getCarList` is being called **after** editing promise got resolved (ie server responded back to the client). – Yury Tarabanko Sep 12 '17 at 06:18
  • Ah.. my mistake.. Your code works well. Maybe there's no way to solve this problem except using chaining or ur way. Thanks a lot! – Canet Robern Sep 12 '17 at 06:43