在服务中调用AngularJS $ http,返回已解析的数据,而不是承诺

时间:2014-12-01 22:59:06

标签: javascript angularjs angular-promise

我想知道是否可以进行使用$http的服务调用,以便直接返回数据而不返回承诺?我试图使用$q而没有任何运气推迟。

这就是我的意思:

我有服务:

angular.module('myModule').factor('myService', ['$http', '$q',
    function($http, $q) {

        // Public API
        return {
            myServiceCall: function() {
                return $http.get('/server/call');
            }
        };
    }
]);

这就是我所说的:

// My controller:
myService.myServiceCall().then(function(data) {
  $scope.data = data;
});

我想避免这种情况,而是希望:

$scope.data = myService.myServiceCall();

我希望它在那条线上完全解决,是否可能?

我尝试过$ q,defer和'然后'的一些组合。方法但似乎无法正确使用,因为该方法立即返回。

修改

如果你想知道为什么,主要的原因是我想简化控制器代码,这很容易用ngResource完成,因为这些调用会在模板中自动解决,所以我想避免做整个& #39;。然后'每次。

并非我不喜欢异步性质,我们的大多数代码都使用它,它只是在一些情况下,同步方式是有帮助的。

我认为,就像你们中的一些人已经指出的那样,我将使用$ resource来获得足够接近的解决方案。

3 个答案:

答案 0 :(得分:9)

我可以通过两种方式来做到这一点。第一个肯定比第二个好。

第一种方式:使用路由的resolve属性

您可以使用路由配置对象的resolve属性来指定必须解析promise并将解析后的值用于其中一个控制器依赖项的值,以便控制器运行时尽快,价值立即可用。

例如,假设您有以下服务:

app.service('things', ['$http', '$q', function ($http, $q) {
    var deferred = $q.defer();

    $http({
        method: 'GET',
        url: '/things',
        cache: true
    }).success(function (data) {
        deferred.resolve(data);
    }).error(function (msg) {
        deferred.reject(msg);
    });

    return deferred.promise;
}]);

然后你可以定义你的路线:

$routeProvider.when('/things', {
    controller: ['$scope', 'thingsData', function ($scope, thingsData) {
        $scope.allThings = thingsData;
    }],
    resolve: {
        thingsData: ['things', function (things) {
            return things;
        }]
    }
});

resolve对象中的键对应于控制器的第二个依赖项的名称,因此即使它返回一个promise,也会在控制器运行之前解析该promise。

第二种方式:在需要数据

之前完成请求

第二种方式,一点都不理想,并且可能会在某些时候咬你的屁股,就是尽早提出数据请求,例如当你的应用程序初始化时,然后实际得到稍后的数据,比如在需要3次点击的页面上从主页进入。手指交叉,如果星星已对齐,那么您的http请求将在此时返回,您将拥有要使用的数据。

angular.module('myModule').factory('myService', ['$http', '$q', function($http, $q) {
    var data = [];

    function fetch() {
        $http.get('/server/call').then(function (d) { data = d; });
    }

    // Public API
    return {
        fetchData: fetch,
        myServiceCall: function () {
            return data;
        }
    };
}]);

然后,在模块的运行功能中,发出请求:

angular.module('myModule').run(['myService', function (myService) {
    myService.fetchData();
});

然后,在应用程序启动后保证至少运行3秒的页面控制器中,您可以按照建议执行操作:

 angular.module('myModule').controller('deepPageCtrl', ['$scope', 'myService', function ($scope, myService) {
      $scope.data = myService.myServiceCall();
      if (angular.isArray($scope.data) && $scope.data.length === 0) {
           panic("data isn't loaded yet");
      }
  }]);

奖金,同样糟糕的第三种方式:同步AJAX请求

按照ricick的建议,并使用jQuery的.ajax()方法使用同步AJAX请求。

答案 1 :(得分:2)

这是不可能的,因为http调用本质上是异步的。也就是说,您需要等待服务器响应。

$ resource服务通过返回一个代理对象来解决这个问题,该代理对象填充了服务器响应中返回的数据,但它仍未在一行中完全解析。

修改

实际上可以使用jquery ajax方法创建同步http请求,方法是设置" async"选项为false。

您可以创建一个包装它的服务,但是您的应用程序将会锁定"当请求等待服务器响应时,因此对于大多数情况,这将是不好的做法。此类请求还有其他限制。

有关更多详细信息,请参阅文档:

http://api.jquery.com/jQuery.ajax/

答案 2 :(得分:0)

很久以前(在有角度的土地上,实际上是在去年的某个时候),它实际上是如何运作的。

但是现在情况已经不再如此了,我认为这是由于视图和控制器中承诺的行为差异,更多的是关于弃用: Angularjs promise not binding to template in 1.2