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

Angular $q execute "then" before http request

几个小时以来,我都快被这个搞疯了。

我有一个 angular 服务工厂可以从我的 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-本地)来管理我的商店 selection。

我在我的视图中添加了 onaftersave='storeChanged(store.id)' 以获取用户编辑的商店 ID select 并且 return 正确地使用了新 ID。

我的storeChanged函数很简单,它基本上运行一个新的请求到API:

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

发生了什么:

一开始,ng-init我看对了the console.log,首先是服务端的,然后是控制器端的。

一旦我 select 我的 select 的另一家商店,我首先看到来自控制器的 console.log,然后是来自服务的

基本上controller里面的数据没有更新,我不明白为什么会这样...

您正在尝试重新解决一个承诺,但您做不到。您只需创建一个 deferred 供每个请求使用。它应该在 addresses 函数内部,以便为每个请求创建一个新函数,但无论如何您都不需要它,因为 $http 已经创建并且 return 已经是一个承诺。您需要 return 来自 $http 的承诺,而不是创建一个新的承诺。请参阅此 post 以获得更好的理解: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;
  });
}

您在服务中全局定义了延迟,因此只有一个全局承诺。因为承诺只能被解决或拒绝一次,所以在您第一次 http 调用后它将永远保持 resolved/rejected。要修复,只需将 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;
    }

  };

}]);

您已经为可能的许多请求创建了一个延迟。您第一次发出请求时它会起作用,但之后它会立即 return,因为您设置的一个承诺已经解决。实际上,与此类似的模式对于缓存非常有用。

$http 已经 return 是一个承诺。您无需特意使用 $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;
            })
        }

    };

}]);