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

iQ.*_*iQ. 12 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');
            }
        };
    }
]);
Run Code Online (Sandbox Code Playgroud)

这就是我所说的:

// My controller:
myService.myServiceCall().then(function(data) {
  $scope.data = data;
});
Run Code Online (Sandbox Code Playgroud)

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

$scope.data = myService.myServiceCall();
Run Code Online (Sandbox Code Playgroud)

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

我已经尝试了$ q,defer和'then'方法的一些组合,但是由于方法立即返回,所以似乎无法正确使用它.

编辑:

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

这并不是说我不喜欢Async本质,我们的大部分代码都使用它,只是在某些情况下,采用同步方式很有帮助.

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

Gre*_*egL 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;
}]);
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样定义你的路线:

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

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 Code Online (Sandbox Code Playgroud)

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

angular.module('myModule').run(['myService', function (myService) {
    myService.fetchData();
});
Run Code Online (Sandbox Code Playgroud)

然后,在应用程序启动后保证至少运行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");
      }
  }]);
Run Code Online (Sandbox Code Playgroud)

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

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


ric*_*ick 2

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

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

编辑:

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

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

请参阅文档了解更多详细信息:

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