【问题标题】:AngularJS $http call in a Service, return resolved data, not promisesAngularJS $http 在服务中调用,返回解析的数据,而不是承诺
【发布时间】:2015-01-30 01:55:41
【问题描述】:

我想知道是否可以使用$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 和 'then' 方法的组合,但似乎无法正确使用,因为该方法会立即返回。

编辑:

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

并不是我不喜欢异步特性,我们的大部分代码都利用了它,只是在某些情况下,采用同步方式会有所帮助。

我认为现在正如你们中的一些人所指出的,我将使用 $resource 来获得足够接近的解决方案。

【问题讨论】:

  • 这是一个异步操作,所以答案是“不是真的”。您可以使用来自ngResource$resource,尽管它会执行一些时髦的内部承诺解决方案,例如$scope.data = $resource('/server/call').get()
  • 至于为什么,我需要使用 'then' 来获取数据,如果它是 ng-resource,它会很好,因为模板会自动解决这些问题。但是对于 $http,我需要每次在控制器上手动解决它,我想我想拥有 ng-resource 的清洁度。

标签: javascript angularjs angular-promise


【解决方案1】:

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

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

您可以使用路由配置对象的resolve 属性来指定必须解析的承诺,并将解析的值用于您的控制器依赖项之一的值,以便一旦您的控制器运行,价值立即可用。

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

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;
        }
    };
}]);

然后,在模块的 run 函数中,发出请求:

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 请求。

【讨论】:

  • 有趣的是我从来不知道第二种方法。
【解决方案2】:

很久以前(在角土地上,实际上是去年的某个时候)它实际上是这样工作的。

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

【讨论】:

    【解决方案3】:

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

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

    编辑:

    实际上可以通过将“async”选项设置为false来使用jquery ajax方法进行同步http请求。

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

    查看文档了解更多详情:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-13
      • 2014-04-12
      • 2017-02-21
      • 1970-01-01
      相关资源
      最近更新 更多