Is there an any method ordering when $q.all has multiple http call functions in Angularjs?
Asked Answered
C

3

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"
            },
    ]
}
Clarkia answered 11/9, 2017 at 9:35 Comment(2)
no, order absolutely kept.Nippon
@Nippon I've thought it has absolute order, but problem was happened..Clarkia
I
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') {
        .....
    });
Iiette answered 11/9, 2017 at 10:18 Comment(1)
umm.. I think this is the best way to all my codes. Thank you. This can help to fix my problem :)Clarkia
E
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

Erdah answered 11/9, 2017 at 9:43 Comment(13)
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?Clarkia
updated answer, changed to built-in, see $q.all({edit: .... getCar: })Erdah
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.Clarkia
@CanetRobern yes, it guarantees. Did you use map instead list? can u post demo in plunker that shows up your problem?Erdah
@CanetRobern take a look on this demo MAP: plnkr.co/edit/t9MHfCyDCBvQ0P7Cyq2d?p=preview and this demo as LIST: plnkr.co/edit/d8mXEAfVnonqh7xBO4Xa?p=previewErdah
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.Clarkia
$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.Clarkia
@CanetRobern not at all. $timeout and $http both return Promise. I think something wrong with your service logic.Erdah
I added my HttpService codes and response of getCarList. Please see this more.Clarkia
@CanetRobern question: do you want 1st call editItem and after get updated getCarList ?Erdah
Yes! Of course! Did my bad English skill make you confused? T.TClarkia
@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 youErdah
Well then, $q.all cannot handle multiple http calls as expected.. Thanks for your interest! It was big helpful for my angular skill :)Clarkia
I
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') {
        .....
    });
Iiette answered 11/9, 2017 at 10:18 Comment(1)
umm.. I think this is the best way to all my codes. Thank you. This can help to fix my problem :)Clarkia
R
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) {

})
Revivify answered 11/9, 2017 at 9:42 Comment(5)
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 guideClarkia
@CanetRobern Then you can't be sure which request will reach your server first. Thus sometime you will experience "dirty reads"Revivify
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 arrayClarkia
@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).Revivify
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!Clarkia

© 2022 - 2024 — McMap. All rights reserved.