Angular $ q执行"然后"在http请求之前

时间:2015-07-24 15:58:57

标签: javascript angularjs promise q

我已经疯了几个小时了。

我有一个角度服务工厂来从我的API获取地址:

App.factory('storesService', ['$http', '$q', 'endpoint', function ($http, $q, endpoint) {

    var deferred = $q.defer();

    return {

        addresses: function (store_id) {
            $http.get(endpoint.store.addresses, {
                params: {
                    id: store_id
                }
            })
            .success(function (data) {
                console.log('Data from API:' + data);
                deferred.resolve(data);
            })
            .error(function () {
                deferred.reject();
            });
            return deferred.promise;
        }

    };

}]);

我的控制器使用此服务来获取特定商店的地址:

$scope.loadAddresses = function (store_id) {
    var load = storesService.addresses(store_id);
    load.then(function (data) {
        console.log('Deferred data:' + data);
        $scope.addresses = data.addresses;
    });
};

在我看来,我有ng-init="loadAddresses(store_id)"store_id是正确的值。

我还使用angular-xeditable(select-local)来管理我的商店选择。

我在视图中添加onaftersave='storeChanged(store.id)'以获取用户选择的商店ID,并正确返回新ID。

我的storeChanged函数非常简单,它基本上会向API发送新请求:

$scope.storeChanged = function (store_id) {
    $scope.loadAddresses(store_id);
};

会发生什么:

一开始,ng-init我正确地看到the console.log,首先是服务中的那个,然后是来自控制器的那个。

一旦我从我的选择中选择了另一个商店,我首先看到控制器中的console.log,然后是服务中的那个。

基本上控制器中的数据没有更新,我无法理解为什么会发生......

3 个答案:

答案 0 :(得分:1)

您正在尝试重新解决您无法做到的承诺。您只需为每个请求创建一个deferred。这应该在addresses函数内部,以便为每个请求创建一个新函数,但无论如何你都不需要它,因为$http已创建并返回一个promise。您需要从$http返回承诺,而不是创建新承诺。有关更好的理解,请参阅此帖子:What is the explicit promise construction antipattern and how do I avoid it?

addresses: function (store_id) {
  return $http.get(endpoint.store.addresses, {
    params: {
      id: store_id
    }
  }).then(function(resp) {
    console.log('Data from API:' + resp.data);
    return resp.data;
  });
}

答案 1 :(得分:1)

您在服务中定义了全局延迟,因此只有一个全局承诺。因为承诺只能被解决或拒绝一次,所以在第一次http调用后它将永远保持解决/拒绝。要修复,只需将行var deferred = $q.defer();移动到您的服务功能中:

App.factory('storesService', ['$http', '$q', 'endpoint', function ($http, $q, endpoint) {

return {

    addresses: function (store_id) {

        var deferred = $q.defer();

        $http.get(endpoint.store.addresses, {
            params: {
                id: store_id
            }
        })
        .success(function (data) {
            console.log('Data from API:' + data);
            deferred.resolve(data);
        })
        .error(function () {
            deferred.reject();
        });
        return deferred.promise;
    }

  };

}]);

答案 2 :(得分:1)

您已为潜在的许多请求创建了一个延迟。第一次提出请求时,它会起作用,但在它之后会立即返回,因为您已设置的承诺已经解决。实际上,smira模式对于缓存非常有用。

$http已经返回一个承诺。您不需要不遗余力地使用$q

App.factory('storesService', ['$http', 'endpoint', function ($http, endpoint) {

    return {

        addresses: function (store_id) {
           return $http.get(endpoint.store.addresses, {
                params: {
                    id: store_id
                }
            }).then(function(response){
               //Chain an extra promise here to clean up the response to just return the data.
               return response.data;
            })
        }

    };

}]);